/*
 * rfbserver.c - deal with server-side of the RFB protocol.
 */

/*
 *  Copyright (C) 2005 Rohit Kumar, Johannes E. Schindelin
 *  Copyright (C) 2002 RealVNC Ltd.
 *  OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
 *  Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.  
 *  All Rights Reserved.
 *
 *  This is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This software is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this software; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
 *  USA.
 */

#ifdef __STRICT_ANSI__
#define _BSD_SOURCE
#endif
#include <string.h>
#include <rfb/rfb.h>
#include <rfb/rfbregion.h>
#include "private.h"

#ifdef LIBVNCSERVER_HAVE_FCNTL_H
#include <fcntl.h>
#endif

#ifdef WIN32
#define write(sock,buf,len) send(sock,buf,len,0)
#else
#ifdef LIBVNCSERVER_HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <pwd.h>
#ifdef LIBVNCSERVER_HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef LIBVNCSERVER_HAVE_NETINET_IN_H
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#endif
#endif

#ifdef CORBA
#include <vncserverctrl.h>
#endif

#ifdef DEBUGPROTO
#undef DEBUGPROTO
#define DEBUGPROTO(x) x
#else
#define DEBUGPROTO(x)
#endif
#include <stdarg.h>
#include <scale.h>
/* stst() */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
/* readdir() */
#include <dirent.h>
/* errno */
#include <errno.h>
/* strftime() */
#include <time.h>

#ifdef __MINGW32__
static int compat_mkdir(const char *path, int mode)
{
	return mkdir(path);
}
#define mkdir compat_mkdir
#endif

static void rfbProcessClientProtocolVersion(rfbClientPtr cl);
static void rfbProcessClientNormalMessage(rfbClientPtr cl);
static void rfbProcessClientInitMessage(rfbClientPtr cl);

#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
void rfbIncrClientRef(rfbClientPtr cl)
{
	LOCK(cl->refCountMutex);
	cl->refCount++;
	UNLOCK(cl->refCountMutex);
}

void rfbDecrClientRef(rfbClientPtr cl)
{
	LOCK(cl->refCountMutex);
	cl->refCount--;
	if(cl->refCount<=0) /* just to be sure also < 0 */
		TSIGNAL(cl->deleteCond);
	UNLOCK(cl->refCountMutex);
}
#else
void rfbIncrClientRef(rfbClientPtr cl) {}
void rfbDecrClientRef(rfbClientPtr cl) {}
#endif

#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
static MUTEX(rfbClientListMutex);
#endif

struct rfbClientIterator {
	rfbClientPtr next;
	rfbScreenInfoPtr screen;
	rfbBool closedToo;
};

void
rfbClientListInit(rfbScreenInfoPtr rfbScreen)
{
	if(sizeof(rfbBool)!=1) {
		/* a sanity check */
		fprintf(stderr,"rfbBool's size is not 1 (%d)!\n",(int)sizeof(rfbBool));
		/* we cannot continue, because rfbBool is supposed to be char everywhere */
		exit(1);
	}
	rfbScreen->clientHead = NULL;
	INIT_MUTEX(rfbClientListMutex);
}

rfbClientIteratorPtr
rfbGetClientIterator(rfbScreenInfoPtr rfbScreen)
{
	rfbClientIteratorPtr i =
			(rfbClientIteratorPtr)malloc(sizeof(struct rfbClientIterator));
	i->next = NULL;
	i->screen = rfbScreen;
	i->closedToo = FALSE;
	return i;
}

rfbClientIteratorPtr
rfbGetClientIteratorWithClosed(rfbScreenInfoPtr rfbScreen)
{
	rfbClientIteratorPtr i =
			(rfbClientIteratorPtr)malloc(sizeof(struct rfbClientIterator));
	i->next = NULL;
	i->screen = rfbScreen;
	i->closedToo = TRUE;
	return i;
}

rfbClientPtr
rfbClientIteratorHead(rfbClientIteratorPtr i)
{
#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
	if(i->next != 0) {
		rfbDecrClientRef(i->next);
		rfbIncrClientRef(i->screen->clientHead);
	}
#endif
	LOCK(rfbClientListMutex);
	i->next = i->screen->clientHead;
	UNLOCK(rfbClientListMutex);
	return i->next;
}

rfbClientPtr
rfbClientIteratorNext(rfbClientIteratorPtr i)
{
	if(i->next == 0) {
		LOCK(rfbClientListMutex);
		i->next = i->screen->clientHead;
		UNLOCK(rfbClientListMutex);
	} else {
		IF_PTHREADS(rfbClientPtr cl = i->next);
		i->next = i->next->next;
		IF_PTHREADS(rfbDecrClientRef(cl));
	}

#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
	if(!i->closedToo)
		while(i->next && i->next->sock<0)
			i->next = i->next->next;
	if(i->next)
		rfbIncrClientRef(i->next);
#endif

	return i->next;
}

void
rfbReleaseClientIterator(rfbClientIteratorPtr iterator)
{
	IF_PTHREADS(if(iterator->next) rfbDecrClientRef(iterator->next));
	free(iterator);
}


/*
 * rfbNewClientConnection is called from sockets.c when a new connection
 * comes in.
 */

void
rfbNewClientConnection(rfbScreenInfoPtr rfbScreen,
		int sock)
{
	rfbClientPtr cl;

	cl = rfbNewClient(rfbScreen,sock);
#ifdef CORBA
	if(cl!=NULL)
		newConnection(cl, (KEYBOARD_DEVICE|POINTER_DEVICE),1,1,1);
#endif
}


/*
 * rfbReverseConnection is called by the CORBA stuff to make an outward
 * connection to a "listening" RFB client.
 */

rfbClientPtr
rfbReverseConnection(rfbScreenInfoPtr rfbScreen,
		char *host,
		int port)
{
	int sock;
	rfbClientPtr cl;

	if ((sock = rfbConnect(rfbScreen, host, port)) < 0)
		return (rfbClientPtr)NULL;

	cl = rfbNewClient(rfbScreen, sock);

	if (cl) {
		cl->reverseConnection = TRUE;
	}

	return cl;
}


void
rfbSetProtocolVersion(rfbScreenInfoPtr rfbScreen, int major_, int minor_)
{
	/* Permit the server to set the version to report */
	/* TODO: sanity checking */
	if ((major_==3) && (minor_ > 2 && minor_ < 9))
	{
		rfbScreen->protocolMajorVersion = major_;
		rfbScreen->protocolMinorVersion = minor_;
	}
	else
		rfbLog("rfbSetProtocolVersion(%d,%d) set to invalid values\n", major_, minor_);
}

/*
 * rfbNewClient is called when a new connection has been made by whatever
 * means.
 */

static rfbClientPtr
rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen,
		int sock,
		rfbBool isUDP)
{
	rfbProtocolVersionMsg pv;
	rfbClientIteratorPtr iterator;
	rfbClientPtr cl,cl_;
	struct sockaddr_in addr;
	socklen_t addrlen = sizeof(struct sockaddr_in);
	rfbProtocolExtension* extension;

	cl = (rfbClientPtr)calloc(sizeof(rfbClientRec),1);

	cl->screen = rfbScreen;
	cl->sock = sock;
	cl->viewOnly = FALSE;
	/* setup pseudo scaling */
	cl->scaledScreen = rfbScreen;
	cl->scaledScreen->scaledScreenRefCount++;

	rfbResetStats(cl);

	cl->clientData = NULL;
	cl->clientGoneHook = rfbDoNothingWithClient;

	if(isUDP) {
		rfbLog(" accepted UDP client\n");
	} else {
		int one=1;

		getpeername(sock, (struct sockaddr *)&addr, &addrlen);
		cl->host = strdup(inet_ntoa(addr.sin_addr));

		rfbLog("  other clients:\n");
		iterator = rfbGetClientIterator(rfbScreen);
		while ((cl_ = rfbClientIteratorNext(iterator)) != NULL) {
			rfbLog("     %s\n",cl_->host);
		}
		rfbReleaseClientIterator(iterator);

#ifndef WIN32
		if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
			rfbLogPerror("fcntl failed");
			close(sock);
			return NULL;
		}
#endif

		if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
				(char *)&one, sizeof(one)) < 0) {
			rfbLogPerror("setsockopt failed");
			close(sock);
			return NULL;
		}

		FD_SET(sock,&(rfbScreen->allFds));
		rfbScreen->maxFd = max(sock,rfbScreen->maxFd);

		INIT_MUTEX(cl->outputMutex);
		INIT_MUTEX(cl->refCountMutex);
		INIT_COND(cl->deleteCond);

		cl->state = RFB_PROTOCOL_VERSION;

		cl->reverseConnection = FALSE;
		cl->readyForSetColourMapEntries = FALSE;
		cl->useCopyRect = FALSE;
		cl->preferredEncoding = -1;
		cl->correMaxWidth = 48;
		cl->correMaxHeight = 48;
#ifdef LIBVNCSERVER_HAVE_LIBZ
		cl->zrleData = NULL;
#endif

		cl->copyRegion = sraRgnCreate();
		cl->copyDX = 0;
		cl->copyDY = 0;

		cl->modifiedRegion =
				sraRgnCreateRect(0,0,rfbScreen->width,rfbScreen->height);

		INIT_MUTEX(cl->updateMutex);
		INIT_COND(cl->updateCond);

		cl->requestedRegion = sraRgnCreate();

		cl->format = cl->screen->serverFormat;
		cl->translateFn = rfbTranslateNone;
		cl->translateLookupTable = NULL;

		LOCK(rfbClientListMutex);

		IF_PTHREADS(cl->refCount = 0);
		cl->next = rfbScreen->clientHead;
		cl->prev = NULL;
		if (rfbScreen->clientHead)
			rfbScreen->clientHead->prev = cl;

		rfbScreen->clientHead = cl;
		UNLOCK(rfbClientListMutex);

#ifdef LIBVNCSERVER_HAVE_LIBZ
		cl->tightQualityLevel = -1;
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
		cl->tightCompressLevel = TIGHT_DEFAULT_COMPRESSION;
		{
			int i;
			for (i = 0; i < 4; i++)
				cl->zsActive[i] = FALSE;
		}
#endif
#endif

		cl->fileTransfer.fd = -1;

		cl->enableCursorShapeUpdates = FALSE;
		cl->enableCursorPosUpdates = FALSE;
		cl->useRichCursorEncoding = FALSE;
		cl->enableLastRectEncoding = FALSE;
		cl->enableKeyboardLedState = FALSE;
		cl->enableSupportedMessages = FALSE;
		cl->enableSupportedEncodings = FALSE;
		cl->enableServerIdentity = FALSE;
		cl->lastKeyboardLedState = -1;
		cl->cursorX = rfbScreen->cursorX;
		cl->cursorY = rfbScreen->cursorY;
		cl->useNewFBSize = FALSE;

#ifdef LIBVNCSERVER_HAVE_LIBZ
		cl->compStreamInited = FALSE;
		cl->compStream.total_in = 0;
		cl->compStream.total_out = 0;
		cl->compStream.zalloc = Z_NULL;
		cl->compStream.zfree = Z_NULL;
		cl->compStream.opaque = Z_NULL;

		cl->zlibCompressLevel = 5;
#endif

		cl->progressiveSliceY = 0;

		cl->extensions = NULL;

		cl->lastPtrX = -1;

		sprintf(pv,rfbProtocolVersionFormat,rfbScreen->protocolMajorVersion,
				rfbScreen->protocolMinorVersion);

		if (rfbWriteExact(cl, pv, sz_rfbProtocolVersionMsg) < 0) {
			rfbLogPerror("rfbNewClient: write");
			rfbCloseClient(cl);
			rfbClientConnectionGone(cl);
			return NULL;
		}
	}

	for(extension = rfbGetExtensionIterator(); extension;
			extension=extension->next) {
		void* data = NULL;
		/* if the extension does not have a newClient method, it wants
		 * to be initialized later. */
		if(extension->newClient && extension->newClient(cl, &data))
			rfbEnableExtension(cl, extension, data);
	}
	rfbReleaseExtensionIterator();

	switch (cl->screen->newClientHook(cl)) {
	case RFB_CLIENT_ON_HOLD:
		cl->onHold = TRUE;
		break;
	case RFB_CLIENT_ACCEPT:
		cl->onHold = FALSE;
		break;
	case RFB_CLIENT_REFUSE:
		rfbCloseClient(cl);
		rfbClientConnectionGone(cl);
		cl = NULL;
		break;
	}
	return cl;
}

rfbClientPtr
rfbNewClient(rfbScreenInfoPtr rfbScreen,
		int sock)
{
	return(rfbNewTCPOrUDPClient(rfbScreen,sock,FALSE));
}

rfbClientPtr
rfbNewUDPClient(rfbScreenInfoPtr rfbScreen)
{
	return((rfbScreen->udpClient=
			rfbNewTCPOrUDPClient(rfbScreen,rfbScreen->udpSock,TRUE)));
}

/*
 * rfbClientConnectionGone is called from sockets.c just after a connection
 * has gone away.
 */

void
rfbClientConnectionGone(rfbClientPtr cl)
{
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
	int i;
#endif

	LOCK(rfbClientListMutex);

	if (cl->prev)
		cl->prev->next = cl->next;
	else
		cl->screen->clientHead = cl->next;
	if (cl->next)
		cl->next->prev = cl->prev;

	if(cl->sock>0)
		close(cl->sock);

	if (cl->scaledScreen!=NULL)
		cl->scaledScreen->scaledScreenRefCount--;

#ifdef LIBVNCSERVER_HAVE_LIBZ
	rfbFreeZrleData(cl);
#endif

	rfbFreeUltraData(cl);

#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
	if(cl->screen->backgroundLoop != FALSE) {
		int i;
		do {
			LOCK(cl->refCountMutex);
			i=cl->refCount;
			if(i>0)
				WAIT(cl->deleteCond,cl->refCountMutex);
			UNLOCK(cl->refCountMutex);
		} while(i>0);
	}
#endif

	UNLOCK(rfbClientListMutex);

	if(cl->sock>=0)
		FD_CLR(cl->sock,&(cl->screen->allFds));

	cl->clientGoneHook(cl);

	rfbLog("Client %s gone\n",cl->host);
	free(cl->host);

#ifdef LIBVNCSERVER_HAVE_LIBZ
	/* Release the compression state structures if any. */
	if ( cl->compStreamInited ) {
		deflateEnd( &(cl->compStream) );
	}

#ifdef LIBVNCSERVER_HAVE_LIBJPEG
	for (i = 0; i < 4; i++) {
		if (cl->zsActive[i])
			deflateEnd(&cl->zsStruct[i]);
	}
#endif
#endif

	if (cl->screen->pointerClient == cl)
		cl->screen->pointerClient = NULL;

	sraRgnDestroy(cl->modifiedRegion);
	sraRgnDestroy(cl->requestedRegion);
	sraRgnDestroy(cl->copyRegion);

	if (cl->translateLookupTable) free(cl->translateLookupTable);

	TINI_COND(cl->updateCond);
	TINI_MUTEX(cl->updateMutex);

	/* make sure outputMutex is unlocked before destroying */
	LOCK(cl->outputMutex);
	UNLOCK(cl->outputMutex);
	TINI_MUTEX(cl->outputMutex);

#ifdef CORBA
	destroyConnection(cl);
#endif

	rfbPrintStats(cl);

	free(cl);
}


/*
 * rfbProcessClientMessage is called when there is data to read from a client.
 */

void
rfbProcessClientMessage(rfbClientPtr cl)
{
	switch (cl->state) {
	case RFB_PROTOCOL_VERSION:
		rfbProcessClientProtocolVersion(cl);
		return;
	case RFB_SECURITY_TYPE:
		rfbProcessClientSecurityType(cl);
		return;
	case RFB_AUTHENTICATION:
		rfbAuthProcessClientMessage(cl);
		return;
	case RFB_INITIALISATION:
		rfbProcessClientInitMessage(cl);
		return;
	default:
		rfbProcessClientNormalMessage(cl);
		return;
	}
}


/*
 * rfbProcessClientProtocolVersion is called when the client sends its
 * protocol version.
 */

static void
rfbProcessClientProtocolVersion(rfbClientPtr cl)
{
	rfbProtocolVersionMsg pv;
	int n, major_, minor_;

	if ((n = rfbReadExact(cl, pv, sz_rfbProtocolVersionMsg)) <= 0) {
		if (n == 0)
			rfbLog("rfbProcessClientProtocolVersion: client gone\n");
		else
			rfbLogPerror("rfbProcessClientProtocolVersion: read");
		rfbCloseClient(cl);
		return;
	}

	pv[sz_rfbProtocolVersionMsg] = 0;
	if (sscanf(pv,rfbProtocolVersionFormat,&major_,&minor_) != 2) {
		char name[1024];
		if(sscanf(pv,"RFB %03d.%03d %1023s\n",&major_,&minor_,name) != 3) {
			rfbErr("rfbProcessClientProtocolVersion: not a valid RFB client: %s\n", pv);
			rfbCloseClient(cl);
			return;
		}
		free(cl->host);
		cl->host=strdup(name);
	}
	rfbLog("Client Protocol Version %d.%d\n", major_, minor_);

	if (major_ != rfbProtocolMajorVersion) {
		rfbErr("RFB protocol version mismatch - server %d.%d, client %d.%d",
				cl->screen->protocolMajorVersion, cl->screen->protocolMinorVersion,
				major_,minor_);
		rfbCloseClient(cl);
		return;
	}

	/* Check for the minor version use either of the two standard version of RFB */
	/*
	 * UltraVNC Viewer detects FileTransfer compatible servers via rfb versions
	 * 3.4, 3.6, 3.14, 3.16
	 * It's a bad method, but it is what they use to enable features...
	 * maintaining RFB version compatibility across multiple servers is a pain
	 * Should use something like ServerIdentity encoding
	 */
	cl->protocolMajorVersion = major_;
	cl->protocolMinorVersion = minor_;

	rfbLog("Protocol version sent %d.%d, using %d.%d\n",
			major_, minor_, rfbProtocolMajorVersion, cl->protocolMinorVersion);

	rfbAuthNewClient(cl);
}


void
rfbClientSendString(rfbClientPtr cl, char *reason)
{
	char *buf;
	int len = strlen(reason);

	rfbLog("rfbClientSendString(\"%s\")\n", reason);

	buf = (char *)malloc(4 + len);
	((uint32_t *)buf)[0] = Swap32IfLE(len);
	memcpy(buf + 4, reason, len);

	if (rfbWriteExact(cl, buf, 4 + len) < 0)
		rfbLogPerror("rfbClientSendString: write");
	free(buf);

	rfbCloseClient(cl);
}

/*
 * rfbClientConnFailed is called when a client connection has failed either
 * because it talks the wrong protocol or it has failed authentication.
 */

void
rfbClientConnFailed(rfbClientPtr cl,
		char *reason)
{
	char *buf;
	int len = strlen(reason);

	rfbLog("rfbClientConnFailed(\"%s\")\n", reason);

	buf = (char *)malloc(8 + len);
	((uint32_t *)buf)[0] = Swap32IfLE(rfbConnFailed);
	((uint32_t *)buf)[1] = Swap32IfLE(len);
	memcpy(buf + 8, reason, len);

	if (rfbWriteExact(cl, buf, 8 + len) < 0)
		rfbLogPerror("rfbClientConnFailed: write");
	free(buf);

	rfbCloseClient(cl);
}


/*
 * rfbProcessClientInitMessage is called when the client sends its
 * initialisation message.
 */

static void
rfbProcessClientInitMessage(rfbClientPtr cl)
{
	rfbClientInitMsg ci;
	union {
		char buf[256];
		rfbServerInitMsg si;
	} u;
	int len, n;
	rfbClientIteratorPtr iterator;
	rfbClientPtr otherCl;
	rfbExtensionData* extension;

	if ((n = rfbReadExact(cl, (char *)&ci,sz_rfbClientInitMsg)) <= 0) {
		if (n == 0)
			rfbLog("rfbProcessClientInitMessage: client gone\n");
		else
			rfbLogPerror("rfbProcessClientInitMessage: read");
		rfbCloseClient(cl);
		return;
	}

	memset(u.buf,0,sizeof(u.buf));

	u.si.framebufferWidth = Swap16IfLE(cl->screen->width);
	u.si.framebufferHeight = Swap16IfLE(cl->screen->height);
	u.si.format = cl->screen->serverFormat;
	u.si.format.redMax = Swap16IfLE(u.si.format.redMax);
	u.si.format.greenMax = Swap16IfLE(u.si.format.greenMax);
	u.si.format.blueMax = Swap16IfLE(u.si.format.blueMax);

	strncpy(u.buf + sz_rfbServerInitMsg, cl->screen->desktopName, 127);
	len = strlen(u.buf + sz_rfbServerInitMsg);
	u.si.nameLength = Swap32IfLE(len);

	if (rfbWriteExact(cl, u.buf, sz_rfbServerInitMsg + len) < 0) {
		rfbLogPerror("rfbProcessClientInitMessage: write");
		rfbCloseClient(cl);
		return;
	}

	for(extension = cl->extensions; extension;) {
		rfbExtensionData* next = extension->next;
		if(extension->extension->init &&
				!extension->extension->init(cl, extension->data))
			/* extension requested that it be removed */
			rfbDisableExtension(cl, extension->extension);
		extension = next;
	}

	cl->state = RFB_NORMAL;

	if (!cl->reverseConnection &&
			(cl->screen->neverShared || (!cl->screen->alwaysShared && !ci.shared))) {

		if (cl->screen->dontDisconnect) {
			iterator = rfbGetClientIterator(cl->screen);
			while ((otherCl = rfbClientIteratorNext(iterator)) != NULL) {
				if ((otherCl != cl) && (otherCl->state == RFB_NORMAL)) {
					rfbLog("-dontdisconnect: Not shared & existing client\n");
					rfbLog("  refusing new client %s\n", cl->host);
					rfbCloseClient(cl);
					rfbReleaseClientIterator(iterator);
					return;
				}
			}
			rfbReleaseClientIterator(iterator);
		} else {
			iterator = rfbGetClientIterator(cl->screen);
			while ((otherCl = rfbClientIteratorNext(iterator)) != NULL) {
				if ((otherCl != cl) && (otherCl->state == RFB_NORMAL)) {
					rfbLog("Not shared - closing connection to client %s\n",
							otherCl->host);
					rfbCloseClient(otherCl);
				}
			}
			rfbReleaseClientIterator(iterator);
		}
	}
}

/* The values come in based on the scaled screen, we need to convert them to
 * values based on the man screen's coordinate system
 */
static rfbBool rectSwapIfLEAndClip(uint16_t* x,uint16_t* y,uint16_t* w,uint16_t* h,
		rfbClientPtr cl)
{
	int x1=Swap16IfLE(*x);
	int y1=Swap16IfLE(*y);
	int w1=Swap16IfLE(*w);
	int h1=Swap16IfLE(*h);

	rfbScaledCorrection(cl->scaledScreen, cl->screen, &x1, &y1, &w1, &h1, "rectSwapIfLEAndClip");
	*x = x1;
	*y = y1;
	*w = w1;
	*h = h1;

	if(*w>cl->screen->width-*x)
		*w=cl->screen->width-*x;
	/* possible underflow */
	if(*w>cl->screen->width-*x)
		return FALSE;
	if(*h>cl->screen->height-*y)
		*h=cl->screen->height-*y;
	if(*h>cl->screen->height-*y)
		return FALSE;

	return TRUE;
}

/*
 * Send keyboard state (PointerPos pseudo-encoding).
 */

rfbBool
rfbSendKeyboardLedState(rfbClientPtr cl)
{
	rfbFramebufferUpdateRectHeader rect;

	if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) {
		if (!rfbSendUpdateBuf(cl))
			return FALSE;
	}

	rect.encoding = Swap32IfLE(rfbEncodingKeyboardLedState);
	rect.r.x = Swap16IfLE(cl->lastKeyboardLedState);
	rect.r.y = 0;
	rect.r.w = 0;
	rect.r.h = 0;

	memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
			sz_rfbFramebufferUpdateRectHeader);
	cl->ublen += sz_rfbFramebufferUpdateRectHeader;

	rfbStatRecordEncodingSent(cl, rfbEncodingKeyboardLedState, sz_rfbFramebufferUpdateRectHeader, sz_rfbFramebufferUpdateRectHeader);

	if (!rfbSendUpdateBuf(cl))
		return FALSE;

	return TRUE;
}


#define rfbSetBit(buffer, position)  (buffer[(position & 255) / 8] |= (1 << (position % 8)))

/*
 * Send rfbEncodingSupportedMessages.
 */

rfbBool
rfbSendSupportedMessages(rfbClientPtr cl)
{
	rfbFramebufferUpdateRectHeader rect;
	rfbSupportedMessages msgs;

	if (cl->ublen + sz_rfbFramebufferUpdateRectHeader
			+ sz_rfbSupportedMessages > UPDATE_BUF_SIZE) {
		if (!rfbSendUpdateBuf(cl))
			return FALSE;
	}

	rect.encoding = Swap32IfLE(rfbEncodingSupportedMessages);
	rect.r.x = 0;
	rect.r.y = 0;
	rect.r.w = Swap16IfLE(sz_rfbSupportedMessages);
	rect.r.h = 0;

	memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
			sz_rfbFramebufferUpdateRectHeader);
	cl->ublen += sz_rfbFramebufferUpdateRectHeader;

	memset((char *)&msgs, 0, sz_rfbSupportedMessages);
	rfbSetBit(msgs.client2server, rfbSetPixelFormat);
	rfbSetBit(msgs.client2server, rfbFixColourMapEntries);
	rfbSetBit(msgs.client2server, rfbSetEncodings);
	rfbSetBit(msgs.client2server, rfbFramebufferUpdateRequest);
	rfbSetBit(msgs.client2server, rfbKeyEvent);
	rfbSetBit(msgs.client2server, rfbPointerEvent);
	rfbSetBit(msgs.client2server, rfbClientCutText);
	rfbSetBit(msgs.client2server, rfbFileTransfer);
	rfbSetBit(msgs.client2server, rfbSetScale);
	/*rfbSetBit(msgs.client2server, rfbSetServerInput);  */
	/*rfbSetBit(msgs.client2server, rfbSetSW);           */
	/*rfbSetBit(msgs.client2server, rfbTextChat);        */
	/*rfbSetBit(msgs.client2server, rfbKeyFrameRequest); */
	rfbSetBit(msgs.client2server, rfbPalmVNCSetScaleFactor);

	rfbSetBit(msgs.server2client, rfbFramebufferUpdate);
	rfbSetBit(msgs.server2client, rfbSetColourMapEntries);
	rfbSetBit(msgs.server2client, rfbBell);
	rfbSetBit(msgs.server2client, rfbServerCutText);
	rfbSetBit(msgs.server2client, rfbResizeFrameBuffer);
	/*rfbSetBit(msgs.server2client, rfbKeyFrameUpdate);  */
	rfbSetBit(msgs.server2client, rfbPalmVNCReSizeFrameBuffer);

	memcpy(&cl->updateBuf[cl->ublen], (char *)&msgs, sz_rfbSupportedMessages);
	cl->ublen += sz_rfbSupportedMessages;

	rfbStatRecordEncodingSent(cl, rfbEncodingSupportedMessages,
			sz_rfbFramebufferUpdateRectHeader+sz_rfbSupportedMessages,
			sz_rfbFramebufferUpdateRectHeader+sz_rfbSupportedMessages);
	if (!rfbSendUpdateBuf(cl))
		return FALSE;

	return TRUE;
}



/*
 * Send rfbEncodingSupportedEncodings.
 */

rfbBool
rfbSendSupportedEncodings(rfbClientPtr cl)
{
	rfbFramebufferUpdateRectHeader rect;
	static uint32_t supported[] = {
			rfbEncodingRaw,
			rfbEncodingCopyRect,
			rfbEncodingRRE,
			rfbEncodingCoRRE,
			rfbEncodingHextile,
#ifdef LIBVNCSERVER_HAVE_LIBZ
			rfbEncodingZlib,
			rfbEncodingZRLE,
			rfbEncodingZYWRLE,
#endif
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
			rfbEncodingTight,
#endif
			rfbEncodingUltra,
			rfbEncodingUltraZip,
			rfbEncodingXCursor,
			rfbEncodingRichCursor,
			rfbEncodingPointerPos,
			rfbEncodingLastRect,
			rfbEncodingNewFBSize,
			rfbEncodingKeyboardLedState,
			rfbEncodingSupportedMessages,
			rfbEncodingSupportedEncodings,
			rfbEncodingServerIdentity,
	};
	uint32_t nEncodings = sizeof(supported) / sizeof(supported[0]), i;

	/* think rfbSetEncodingsMsg */

	if (cl->ublen + sz_rfbFramebufferUpdateRectHeader
			+ (nEncodings * sizeof(uint32_t)) > UPDATE_BUF_SIZE) {
		if (!rfbSendUpdateBuf(cl))
			return FALSE;
	}

	rect.encoding = Swap32IfLE(rfbEncodingSupportedEncodings);
	rect.r.x = 0;
	rect.r.y = 0;
	rect.r.w = Swap16IfLE(nEncodings * sizeof(uint32_t));
	rect.r.h = Swap16IfLE(nEncodings);

	memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
			sz_rfbFramebufferUpdateRectHeader);
	cl->ublen += sz_rfbFramebufferUpdateRectHeader;

	for (i = 0; i < nEncodings; i++) {
		uint32_t encoding = Swap32IfLE(supported[i]);
		memcpy(&cl->updateBuf[cl->ublen], (char *)&encoding, sizeof(encoding));
		cl->ublen += sizeof(encoding);
	}

	rfbStatRecordEncodingSent(cl, rfbEncodingSupportedEncodings,
			sz_rfbFramebufferUpdateRectHeader+(nEncodings * sizeof(uint32_t)),
			sz_rfbFramebufferUpdateRectHeader+(nEncodings * sizeof(uint32_t)));

	if (!rfbSendUpdateBuf(cl))
		return FALSE;

	return TRUE;
}


void
rfbSetServerVersionIdentity(rfbScreenInfoPtr screen, char *fmt, ...)
{
	char buffer[256];
	va_list ap;

	va_start(ap, fmt);
	vsnprintf(buffer, sizeof(buffer)-1, fmt, ap);
	va_end(ap);

	if (screen->versionString!=NULL) free(screen->versionString);
	screen->versionString = strdup(buffer);
}

/*
 * Send rfbEncodingServerIdentity.
 */

rfbBool
rfbSendServerIdentity(rfbClientPtr cl)
{
	rfbFramebufferUpdateRectHeader rect;
	char buffer[512];

	/* tack on our library version */
	snprintf(buffer,sizeof(buffer)-1, "%s (%s)",
			(cl->screen->versionString==NULL ? "unknown" : cl->screen->versionString),
			LIBVNCSERVER_PACKAGE_STRING);

	if (cl->ublen + sz_rfbFramebufferUpdateRectHeader
			+ (strlen(buffer)+1) > UPDATE_BUF_SIZE) {
		if (!rfbSendUpdateBuf(cl))
			return FALSE;
	}

	rect.encoding = Swap32IfLE(rfbEncodingServerIdentity);
	rect.r.x = 0;
	rect.r.y = 0;
	rect.r.w = Swap16IfLE(strlen(buffer)+1);
	rect.r.h = 0;

	memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
			sz_rfbFramebufferUpdateRectHeader);
	cl->ublen += sz_rfbFramebufferUpdateRectHeader;

	memcpy(&cl->updateBuf[cl->ublen], buffer, strlen(buffer)+1);
	cl->ublen += strlen(buffer)+1;

	rfbStatRecordEncodingSent(cl, rfbEncodingServerIdentity,
			sz_rfbFramebufferUpdateRectHeader+strlen(buffer)+1,
			sz_rfbFramebufferUpdateRectHeader+strlen(buffer)+1);


	if (!rfbSendUpdateBuf(cl))
		return FALSE;

	return TRUE;
}

rfbBool rfbSendTextChatMessage(rfbClientPtr cl, uint32_t length, char *buffer)
{
	rfbTextChatMsg tc;
	int bytesToSend=0;

	memset((char *)&tc, 0, sizeof(tc));
	tc.type = rfbTextChat;
	tc.length = Swap32IfLE(length);

	switch(length) {
	case rfbTextChatOpen:
	case rfbTextChatClose:
	case rfbTextChatFinished:
		bytesToSend=0;
		break;
	default:
		bytesToSend=length;
		if (bytesToSend>rfbTextMaxSize)
			bytesToSend=rfbTextMaxSize;
	}

	if (cl->ublen + sz_rfbTextChatMsg + bytesToSend > UPDATE_BUF_SIZE) {
		if (!rfbSendUpdateBuf(cl))
			return FALSE;
	}

	memcpy(&cl->updateBuf[cl->ublen], (char *)&tc, sz_rfbTextChatMsg);
	cl->ublen += sz_rfbTextChatMsg;
	if (bytesToSend>0) {
		memcpy(&cl->updateBuf[cl->ublen], buffer, bytesToSend);
		cl->ublen += bytesToSend;
	}
	rfbStatRecordMessageSent(cl, rfbTextChat, sz_rfbTextChatMsg+bytesToSend, sz_rfbTextChatMsg+bytesToSend);

	if (!rfbSendUpdateBuf(cl))
		return FALSE;

	return TRUE;
}

#define FILEXFER_ALLOWED_OR_CLOSE_AND_RETURN(msg, cl, ret) \
		if ((cl->screen->getFileTransferPermission != NULL \
				&& cl->screen->getFileTransferPermission(cl) != TRUE) \
				|| cl->screen->permitFileTransfer != TRUE) { \
			rfbLog("%sUltra File Transfer is disabled, dropping client: %s\n", msg, cl->host); \
			rfbCloseClient(cl); \
			return ret; \
		}

int DB = 1;

rfbBool rfbSendFileTransferMessage(rfbClientPtr cl, uint8_t contentType, uint8_t contentParam, uint32_t size, uint32_t length, char *buffer)
{
	rfbFileTransferMsg ft;
	ft.type = rfbFileTransfer;
	ft.contentType = contentType;
	ft.contentParam = contentParam;
	ft.pad          = 0; /* UltraVNC did not Swap16LE(ft.contentParam) (Looks like it might be BigEndian) */
	ft.size         = Swap32IfLE(size);
	ft.length       = Swap32IfLE(length);

	FILEXFER_ALLOWED_OR_CLOSE_AND_RETURN("", cl, FALSE);
	/*
    rfbLog("rfbSendFileTransferMessage( %dtype, %dparam, %dsize, %dlen, %p)\n", contentType, contentParam, size, length, buffer);
	 */
	if (rfbWriteExact(cl, (char *)&ft, sz_rfbFileTransferMsg) < 0) {
		rfbLogPerror("rfbSendFileTransferMessage: write");
		rfbCloseClient(cl);
		return FALSE;
	}

	if (length>0)
	{
		if (rfbWriteExact(cl, buffer, length) < 0) {
			rfbLogPerror("rfbSendFileTransferMessage: write");
			rfbCloseClient(cl);
			return FALSE;
		}
	}

	rfbStatRecordMessageSent(cl, rfbFileTransfer, sz_rfbFileTransferMsg+length, sz_rfbFileTransferMsg+length);

	return TRUE;
}


/*
 * UltraVNC uses Windows Structures
 */
#define MAX_PATH 260

typedef struct {
	uint32_t dwLowDateTime;
	uint32_t dwHighDateTime;
} RFB_FILETIME; 

typedef struct {
	uint32_t dwFileAttributes;
	RFB_FILETIME ftCreationTime;
	RFB_FILETIME ftLastAccessTime;
	RFB_FILETIME ftLastWriteTime;
	uint32_t nFileSizeHigh;
	uint32_t nFileSizeLow;
	uint32_t dwReserved0;
	uint32_t dwReserved1;
	uint8_t  cFileName[ MAX_PATH ];
	uint8_t  cAlternateFileName[ 14 ];
} RFB_FIND_DATA;

#define RFB_FILE_ATTRIBUTE_READONLY   0x1
#define RFB_FILE_ATTRIBUTE_HIDDEN     0x2
#define RFB_FILE_ATTRIBUTE_SYSTEM     0x4
#define RFB_FILE_ATTRIBUTE_DIRECTORY  0x10
#define RFB_FILE_ATTRIBUTE_ARCHIVE    0x20
#define RFB_FILE_ATTRIBUTE_NORMAL     0x80
#define RFB_FILE_ATTRIBUTE_TEMPORARY  0x100
#define RFB_FILE_ATTRIBUTE_COMPRESSED 0x800

rfbBool rfbFilenameTranslate2UNIX(rfbClientPtr cl, char *path, char *unixPath)
{
	int x;
	char *home=NULL;

	FILEXFER_ALLOWED_OR_CLOSE_AND_RETURN("", cl, FALSE);

	/* C: */
	if (path[0]=='C' && path[1]==':')
		strcpy(unixPath, &path[2]);
	else
	{
		home = getenv("HOME");
		if (home!=NULL)
		{
			strcpy(unixPath, home);
			strcat(unixPath,"/");
			strcat(unixPath, path);
		}
		else
			strcpy(unixPath, path);
	}
	for (x=0;x<(int)strlen(unixPath);x++)
		if (unixPath[x]=='\\') unixPath[x]='/';
	return TRUE;
}

rfbBool rfbFilenameTranslate2DOS(rfbClientPtr cl, char *unixPath, char *path)
{
	int x;

	FILEXFER_ALLOWED_OR_CLOSE_AND_RETURN("", cl, FALSE);

	sprintf(path,"C:%s", unixPath);
	for (x=2;x<(int)strlen(path);x++)
		if (path[x]=='/') path[x]='\\';
	return TRUE;
}

rfbBool rfbSendDirContent(rfbClientPtr cl, int length, char *buffer)
{
	char retfilename[MAX_PATH];
	char path[MAX_PATH];
	struct stat statbuf;
	RFB_FIND_DATA win32filename;
	int nOptLen = 0, retval=0;
	DIR *dirp=NULL;
	struct dirent *direntp=NULL;

	FILEXFER_ALLOWED_OR_CLOSE_AND_RETURN("", cl, FALSE);

	/* Client thinks we are Winblows */
	rfbFilenameTranslate2UNIX(cl, buffer, path);

	if (DB) rfbLog("rfbProcessFileTransfer() rfbDirContentRequest: rfbRDirContent: \"%s\"->\"%s\"\n",buffer, path);

	dirp=opendir(path);
	if (dirp==NULL)
		return rfbSendFileTransferMessage(cl, rfbDirPacket, rfbADirectory, 0, 0, NULL);
	/* send back the path name (necessary for links) */
	if (rfbSendFileTransferMessage(cl, rfbDirPacket, rfbADirectory, 0, length, buffer)==FALSE) return FALSE;
	for (direntp=readdir(dirp); direntp!=NULL; direntp=readdir(dirp))
	{
		/* get stats */
		snprintf(retfilename,sizeof(retfilename),"%s/%s", path, direntp->d_name);
		retval = stat(retfilename, &statbuf);

		if (retval==0)
		{
			memset((char *)&win32filename, 0, sizeof(win32filename));
			win32filename.dwFileAttributes = Swap32IfBE(RFB_FILE_ATTRIBUTE_NORMAL);
			if (S_ISDIR(statbuf.st_mode))
				win32filename.dwFileAttributes = Swap32IfBE(RFB_FILE_ATTRIBUTE_DIRECTORY);
			win32filename.ftCreationTime.dwLowDateTime = Swap32IfBE(statbuf.st_ctime);   /* Intel Order */
			win32filename.ftCreationTime.dwHighDateTime = 0;
			win32filename.ftLastAccessTime.dwLowDateTime = Swap32IfBE(statbuf.st_atime); /* Intel Order */
			win32filename.ftLastAccessTime.dwHighDateTime = 0;
			win32filename.ftLastWriteTime.dwLowDateTime = Swap32IfBE(statbuf.st_mtime);  /* Intel Order */
			win32filename.ftLastWriteTime.dwHighDateTime = 0;
			win32filename.nFileSizeLow = Swap32IfBE(statbuf.st_size); /* Intel Order */
			win32filename.nFileSizeHigh = 0;
			win32filename.dwReserved0 = 0;
			win32filename.dwReserved1 = 0;

			/* If this had the full path, we would need to translate to DOS format ("C:\") */
			/* rfbFilenameTranslate2DOS(cl, retfilename, win32filename.cFileName); */
			strcpy((char *)win32filename.cFileName, direntp->d_name);

			/* Do not show hidden files (but show how to move up the tree) */
			if ((strcmp(direntp->d_name, "..")==0) || (direntp->d_name[0]!='.'))
			{
				nOptLen = sizeof(RFB_FIND_DATA) - MAX_PATH - 14 + strlen((char *)win32filename.cFileName);
				/*
                rfbLog("rfbProcessFileTransfer() rfbDirContentRequest: rfbRDirContent: Sending \"%s\"\n", (char *)win32filename.cFileName);
				 */
				if (rfbSendFileTransferMessage(cl, rfbDirPacket, rfbADirectory, 0, nOptLen, (char *)&win32filename)==FALSE) return FALSE;
			}
		}
	}
	closedir(dirp);
	/* End of the transfer */
	return rfbSendFileTransferMessage(cl, rfbDirPacket, 0, 0, 0, NULL);
}


char *rfbProcessFileTransferReadBuffer(rfbClientPtr cl, uint32_t length)
{
	char *buffer=NULL;
	int   n=0;

	FILEXFER_ALLOWED_OR_CLOSE_AND_RETURN("", cl, NULL);
	/*
    rfbLog("rfbProcessFileTransferReadBuffer(%dlen)\n", length);
	 */
	if (length>0) {
		buffer=malloc(length+1);
		if (buffer!=NULL) {
			if ((n = rfbReadExact(cl, (char *)buffer, length)) <= 0) {
				if (n != 0)
					rfbLogPerror("rfbProcessFileTransferReadBuffer: read");
				rfbCloseClient(cl);
				/* NOTE: don't forget to free(buffer) if you return early! */
				if (buffer!=NULL) free(buffer);
				return NULL;
			}
			/* Null Terminate */
			buffer[length]=0;
		}
	}
	return buffer;
}


rfbBool rfbSendFileTransferChunk(rfbClientPtr cl)
{
	/* Allocate buffer for compression */
	unsigned char readBuf[sz_rfbBlockSize];
	int bytesRead=0;
	int retval=0;
	fd_set wfds;
	struct timeval tv;
	int n;
#ifdef LIBVNCSERVER_HAVE_LIBZ
	unsigned char compBuf[sz_rfbBlockSize + 1024];
	unsigned long nMaxCompSize = sizeof(compBuf);
	int nRetC = 0;
#endif

	/*
	 * Don't close the client if we get into this one because
	 * it is called from many places to service file transfers.
	 * Note that permitFileTransfer is checked first.
	 */
	if (cl->screen->permitFileTransfer != TRUE ||
			(cl->screen->getFileTransferPermission != NULL
					&& cl->screen->getFileTransferPermission(cl) != TRUE)) {
		return TRUE;
	}

	/* If not sending, or no file open...   Return as if we sent something! */
	if ((cl->fileTransfer.fd!=-1) && (cl->fileTransfer.sending==1))
	{
		FD_ZERO(&wfds);
		FD_SET(cl->sock, &wfds);

		/* return immediately */
		tv.tv_sec = 0;
		tv.tv_usec = 0;
		n = select(cl->sock + 1, NULL, &wfds, NULL, &tv);

		if (n<0) {
			rfbLog("rfbSendFileTransferChunk() select failed: %s\n", strerror(errno));
		}
		/* We have space on the transmit queue */
		if (n > 0)
		{
			bytesRead = read(cl->fileTransfer.fd, readBuf, sz_rfbBlockSize);
			switch (bytesRead) {
			case 0:
				/*
                rfbLog("rfbSendFileTransferChunk(): End-Of-File Encountered\n");
				 */
				retval = rfbSendFileTransferMessage(cl, rfbEndOfFile, 0, 0, 0, NULL);
				close(cl->fileTransfer.fd);
				cl->fileTransfer.fd = -1;
				cl->fileTransfer.sending   = 0;
				cl->fileTransfer.receiving = 0;
				return retval;
			case -1:
			/* TODO : send an error msg to the client... */
			rfbLog("rfbSendFileTransferChunk(): %s\n",strerror(errno));
			retval = rfbSendFileTransferMessage(cl, rfbAbortFileTransfer, 0, 0, 0, NULL);
			close(cl->fileTransfer.fd);
			cl->fileTransfer.fd = -1;
			cl->fileTransfer.sending   = 0;
			cl->fileTransfer.receiving = 0;
			return retval;
			default:
				/*
                rfbLog("rfbSendFileTransferChunk(): Read %d bytes\n", bytesRead);
				 */
				if (!cl->fileTransfer.compressionEnabled)
					return  rfbSendFileTransferMessage(cl, rfbFilePacket, 0, 0, bytesRead, (char *)readBuf);
				else
				{
#ifdef LIBVNCSERVER_HAVE_LIBZ
					nRetC = compress(compBuf, &nMaxCompSize, readBuf, bytesRead);
					/*
                    rfbLog("Compressed the packet from %d -> %d bytes\n", nMaxCompSize, bytesRead);
					 */

					if ((nRetC==0) && ((int)nMaxCompSize<bytesRead))
						return  rfbSendFileTransferMessage(cl, rfbFilePacket, 0, 1, nMaxCompSize, (char *)compBuf);
					else
						return  rfbSendFileTransferMessage(cl, rfbFilePacket, 0, 0, bytesRead, (char *)readBuf);
#else
					/* We do not support compression of the data stream */
					return  rfbSendFileTransferMessage(cl, rfbFilePacket, 0, 0, bytesRead, (char *)readBuf);
#endif
				}
			}
		}
	}
	return TRUE;
}

rfbBool rfbProcessFileTransfer(rfbClientPtr cl, uint8_t contentType, uint8_t contentParam, uint32_t size, uint32_t length)
{
	char *buffer=NULL, *p=NULL;
	int retval=0;
	char filename1[MAX_PATH];
	char filename2[MAX_PATH];
	char szFileTime[MAX_PATH];
	struct stat statbuf;
	uint32_t sizeHtmp=0;
	int n=0;
	char timespec[64];
#ifdef LIBVNCSERVER_HAVE_LIBZ
	unsigned char compBuff[sz_rfbBlockSize];
	unsigned long nRawBytes = sz_rfbBlockSize;
	int nRet = 0;
#endif

	FILEXFER_ALLOWED_OR_CLOSE_AND_RETURN("", cl, FALSE);

	/*
    rfbLog("rfbProcessFileTransfer(%dtype, %dparam, %dsize, %dlen)\n", contentType, contentParam, size, length);
	 */

	switch (contentType) {
	case rfbDirContentRequest:
		switch (contentParam) {
		case rfbRDrivesList: /* Client requests the List of Local Drives */
			/*
            rfbLog("rfbProcessFileTransfer() rfbDirContentRequest: rfbRDrivesList:\n");
			 */
			/* Format when filled : "C:\<NULL>D:\<NULL>....Z:\<NULL><NULL>
			 *
			 * We replace the "\" char following the drive letter and ":"
			 * with a char corresponding to the type of drive
			 * We obtain something like "C:l<NULL>D:c<NULL>....Z:n\<NULL><NULL>"
			 *  Isn't it ugly ?
			 * DRIVE_FIXED = 'l'     (local?)
			 * DRIVE_REMOVABLE = 'f' (floppy?)
			 * DRIVE_CDROM = 'c'
			 * DRIVE_REMOTE = 'n'
			 */

			/* in unix, there are no 'drives'  (We could list mount points though)
			 * We fake the root as a "C:" for the Winblows users
			 */
			filename2[0]='C';
			filename2[1]=':';
			filename2[2]='l';
			filename2[3]=0;
			filename2[4]=0;
			retval = rfbSendFileTransferMessage(cl, rfbDirPacket, rfbADrivesList, 0, 5, filename2);
			if (buffer!=NULL) free(buffer);
			return retval;
			break;
		case rfbRDirContent: /* Client requests the content of a directory */
			/*
            rfbLog("rfbProcessFileTransfer() rfbDirContentRequest: rfbRDirContent\n");
			 */
			if ((buffer = rfbProcessFileTransferReadBuffer(cl, length))==NULL) return FALSE;
			retval = rfbSendDirContent(cl, length, buffer);
			if (buffer!=NULL) free(buffer);
			return retval;
		}
		break;

		case rfbDirPacket:
			rfbLog("rfbProcessFileTransfer() rfbDirPacket\n");
			break;
		case rfbFileAcceptHeader:
			rfbLog("rfbProcessFileTransfer() rfbFileAcceptHeader\n");
			break;
		case rfbCommandReturn:
			rfbLog("rfbProcessFileTransfer() rfbCommandReturn\n");
			break;
		case rfbFileChecksums:
			/* Destination file already exists - the viewer sends the checksums */
			rfbLog("rfbProcessFileTransfer() rfbFileChecksums\n");
			break;
		case rfbFileTransferAccess:
			rfbLog("rfbProcessFileTransfer() rfbFileTransferAccess\n");
			break;

			/*
			 * sending from the server to the viewer
			 */

		case rfbFileTransferRequest:
			/*
        rfbLog("rfbProcessFileTransfer() rfbFileTransferRequest:\n");
			 */
			/* add some space to the end of the buffer as we will be adding a timespec to it */
			if ((buffer = rfbProcessFileTransferReadBuffer(cl, length))==NULL) return FALSE;
			/* The client requests a File */
			rfbFilenameTranslate2UNIX(cl, buffer, filename1);
			cl->fileTransfer.fd=open(filename1, O_RDONLY, 0744);

			/*
			 */
			if (DB) rfbLog("rfbProcessFileTransfer() rfbFileTransferRequest(\"%s\"->\"%s\") Open: %s fd=%d\n", buffer, filename1, (cl->fileTransfer.fd==-1?"Failed":"Success"), cl->fileTransfer.fd);

			if (cl->fileTransfer.fd!=-1) {
				if (fstat(cl->fileTransfer.fd, &statbuf)!=0) {
					close(cl->fileTransfer.fd);
					cl->fileTransfer.fd=-1;
				}
				else
				{
					/* Add the File Time Stamp to the filename */
					strftime(timespec, sizeof(timespec), "%m/%d/%Y %H:%M",gmtime((time_t *)&statbuf.st_ctime));
					buffer=realloc(buffer, length + strlen(timespec) + 2); /* comma, and Null term */
					if (buffer==NULL) {
						rfbLog("rfbProcessFileTransfer() rfbFileTransferRequest: Failed to malloc %d bytes\n", length + strlen(timespec) + 2);
						return FALSE;
					}
					strcat(buffer,",");
					strcat(buffer, timespec);
					length = strlen(buffer);
					if (DB) rfbLog("rfbProcessFileTransfer() buffer is now: \"%s\"\n", buffer);
				}
			}

			/* The viewer supports compression if size==1 */
			cl->fileTransfer.compressionEnabled = (size==1);

			/*
        rfbLog("rfbProcessFileTransfer() rfbFileTransferRequest(\"%s\"->\"%s\")%s\n", buffer, filename1, (size==1?" <Compression Enabled>":""));
			 */

			/* File Size in bytes, 0xFFFFFFFF (-1) means error */
			retval = rfbSendFileTransferMessage(cl, rfbFileHeader, 0, (cl->fileTransfer.fd==-1 ? -1 : statbuf.st_size), length, buffer);

			if (cl->fileTransfer.fd==-1)
			{
				if (buffer!=NULL) free(buffer);
				return retval;
			}
			/* setup filetransfer stuff */
			cl->fileTransfer.fileSize = statbuf.st_size;
			cl->fileTransfer.numPackets = statbuf.st_size / sz_rfbBlockSize;
			cl->fileTransfer.receiving = 0;
			cl->fileTransfer.sending = 0; /* set when we receive a rfbFileHeader: */

			/* TODO: finish 64-bit file size support */
			sizeHtmp = 0;
			if (rfbWriteExact(cl, (char *)&sizeHtmp, 4) < 0) {
				rfbLogPerror("rfbProcessFileTransfer: write");
				rfbCloseClient(cl);
				if (buffer!=NULL) free(buffer);
				return FALSE;
			}
			break;

		case rfbFileHeader:
			/* Destination file (viewer side) is ready for reception (size > 0) or not (size = -1) */
			if ((int32_t)size==-1) {
				rfbLog("rfbProcessFileTransfer() rfbFileHeader (error, aborting)\n");
				close(cl->fileTransfer.fd);
				cl->fileTransfer.fd=-1;
				return TRUE;
			}

			/*
        rfbLog("rfbProcessFileTransfer() rfbFileHeader (%d bytes of a file)\n", size);
			 */

			/* Starts the transfer! */
			cl->fileTransfer.sending=1;
			return rfbSendFileTransferChunk(cl);
			break;


			/*
			 * sending from the viewer to the server
			 */

		case rfbFileTransferOffer:
			/* client is sending a file to us */
			/* buffer contains full path name (plus FileTime) */
			/* size contains size of the file */
			/*
        rfbLog("rfbProcessFileTransfer() rfbFileTransferOffer:\n");
			 */
			if ((buffer = rfbProcessFileTransferReadBuffer(cl, length))==NULL) return FALSE;

			/* Parse the FileTime */
			p = strrchr(buffer, ',');
			if (p!=NULL) {
				*p = '\0';
				strcpy(szFileTime, p+1);
			} else
				szFileTime[0]=0;



			/* Need to read in sizeHtmp */
			if ((n = rfbReadExact(cl, (char *)&sizeHtmp, 4)) <= 0) {
				if (n != 0)
					rfbLogPerror("rfbProcessFileTransfer: read sizeHtmp");
				rfbCloseClient(cl);
				/* NOTE: don't forget to free(buffer) if you return early! */
				if (buffer!=NULL) free(buffer);
				return FALSE;
			}
			sizeHtmp = Swap32IfLE(sizeHtmp);

			rfbFilenameTranslate2UNIX(cl, buffer, filename1);

			/* If the file exists... We can send a rfbFileChecksums back to the client before we send an rfbFileAcceptHeader */
			/* TODO: Delta Transfer */

			cl->fileTransfer.fd=open(filename1, O_CREAT|O_WRONLY|O_TRUNC, 0744);
			if (DB) rfbLog("rfbProcessFileTransfer() rfbFileTransferOffer(\"%s\"->\"%s\") %s %s fd=%d\n", buffer, filename1, (cl->fileTransfer.fd==-1?"Failed":"Success"), (cl->fileTransfer.fd==-1?strerror(errno):""), cl->fileTransfer.fd);
			/*
			 */

			/* File Size in bytes, 0xFFFFFFFF (-1) means error */
			retval = rfbSendFileTransferMessage(cl, rfbFileAcceptHeader, 0, (cl->fileTransfer.fd==-1 ? -1 : 0), length, buffer);
			if (cl->fileTransfer.fd==-1) {
				free(buffer);
				return retval;
			}

			/* setup filetransfer stuff */
			cl->fileTransfer.fileSize = size;
			cl->fileTransfer.numPackets = size / sz_rfbBlockSize;
			cl->fileTransfer.receiving = 1;
			cl->fileTransfer.sending = 0;
			break;

		case rfbFilePacket:
			/*
        rfbLog("rfbProcessFileTransfer() rfbFilePacket:\n");
			 */
			if ((buffer = rfbProcessFileTransferReadBuffer(cl, length))==NULL) return FALSE;
			if (cl->fileTransfer.fd!=-1) {
				/* buffer contains the contents of the file */
				if (size==0)
					retval=write(cl->fileTransfer.fd, buffer, length);
				else
				{
#ifdef LIBVNCSERVER_HAVE_LIBZ
					/* compressed packet */
					nRet = uncompress(compBuff,&nRawBytes,(const unsigned char*)buffer, length);
					retval=write(cl->fileTransfer.fd, compBuff, nRawBytes);
#else
					/* Write the file out as received... */
					retval=write(cl->fileTransfer.fd, buffer, length);
#endif
				}
				if (retval==-1)
				{
					close(cl->fileTransfer.fd);
					cl->fileTransfer.fd=-1;
					cl->fileTransfer.sending   = 0;
					cl->fileTransfer.receiving = 0;
				}
			}
			break;

		case rfbEndOfFile:
			if (DB) rfbLog("rfbProcessFileTransfer() rfbEndOfFile\n");
			/*
			 */
			if (cl->fileTransfer.fd!=-1)
				close(cl->fileTransfer.fd);
			cl->fileTransfer.fd=-1;
			cl->fileTransfer.sending   = 0;
			cl->fileTransfer.receiving = 0;
			break;

		case rfbAbortFileTransfer:
			if (DB) rfbLog("rfbProcessFileTransfer() rfbAbortFileTransfer\n");
			/*
			 */
			if (cl->fileTransfer.fd!=-1)
			{
				close(cl->fileTransfer.fd);
				cl->fileTransfer.fd=-1;
				cl->fileTransfer.sending   = 0;
				cl->fileTransfer.receiving = 0;
			}
			else
			{
				/* We use this message for FileTransfer rights (<=RC18 versions)
				 * The client asks for FileTransfer permission
				 */
				if (contentParam == 0)
				{
					rfbLog("rfbProcessFileTransfer() File Transfer Permission DENIED! (Client Version <=RC18)\n");
					/* Old method for FileTransfer handshake perimssion (<=RC18) (Deny it)*/
					return rfbSendFileTransferMessage(cl, rfbAbortFileTransfer, 0, -1, 0, "");
				}
				/* New method is allowed */
				if (cl->screen->getFileTransferPermission!=NULL)
				{
					if (cl->screen->getFileTransferPermission(cl)==TRUE)
					{
						rfbLog("rfbProcessFileTransfer() File Transfer Permission Granted!\n");
						return rfbSendFileTransferMessage(cl, rfbFileTransferAccess, 0, 1 , 0, ""); /* Permit */
					}
					else
					{
						rfbLog("rfbProcessFileTransfer() File Transfer Permission DENIED!\n");
						return rfbSendFileTransferMessage(cl, rfbFileTransferAccess, 0, -1 , 0, ""); /* Deny */
					}
				}
				else
				{
					if (cl->screen->permitFileTransfer)
					{
						rfbLog("rfbProcessFileTransfer() File Transfer Permission Granted!\n");
						return rfbSendFileTransferMessage(cl, rfbFileTransferAccess, 0, 1 , 0, ""); /* Permit */
					}
					else
					{
						rfbLog("rfbProcessFileTransfer() File Transfer Permission DENIED by default!\n");
						return rfbSendFileTransferMessage(cl, rfbFileTransferAccess, 0, -1 , 0, ""); /* DEFAULT: DENY (for security) */
					}

				}
			}
			break;


		case rfbCommand:
			/*
        rfbLog("rfbProcessFileTransfer() rfbCommand:\n");
			 */
			if ((buffer = rfbProcessFileTransferReadBuffer(cl, length))==NULL) return FALSE;
			switch (contentParam) {
			case rfbCDirCreate:  /* Client requests the creation of a directory */
				rfbFilenameTranslate2UNIX(cl, buffer, filename1);
				retval = mkdir(filename1, 0755);
				if (DB) rfbLog("rfbProcessFileTransfer() rfbCommand: rfbCDirCreate(\"%s\"->\"%s\") %s\n", buffer, filename1, (retval==-1?"Failed":"Success"));
				/*
				 */
				retval = rfbSendFileTransferMessage(cl, rfbCommandReturn, rfbADirCreate, retval, length, buffer);
				if (buffer!=NULL) free(buffer);
				return retval;
			case rfbCFileDelete: /* Client requests the deletion of a file */
				rfbFilenameTranslate2UNIX(cl, buffer, filename1);
				if (stat(filename1,&statbuf)==0)
				{
					if (S_ISDIR(statbuf.st_mode))
						retval = rmdir(filename1);
					else
						retval = unlink(filename1);
				}
				else retval=-1;
				retval = rfbSendFileTransferMessage(cl, rfbCommandReturn, rfbAFileDelete, retval, length, buffer);
				if (buffer!=NULL) free(buffer);
				return retval;
			case rfbCFileRename: /* Client requests the Renaming of a file/directory */
				p = strrchr(buffer, '*');
				if (p != NULL)
				{
					/* Split into 2 filenames ('*' is a seperator) */
					*p = '\0';
					rfbFilenameTranslate2UNIX(cl, buffer, filename1);
					rfbFilenameTranslate2UNIX(cl, p+1,    filename2);
					retval = rename(filename1,filename2);
					if (DB) rfbLog("rfbProcessFileTransfer() rfbCommand: rfbCFileRename(\"%s\"->\"%s\" -->> \"%s\"->\"%s\") %s\n", buffer, filename1, p+1, filename2, (retval==-1?"Failed":"Success"));
					/*
					 */
					/* Restore the buffer so the reply is good */
					*p = '*';
					retval = rfbSendFileTransferMessage(cl, rfbCommandReturn, rfbAFileRename, retval, length, buffer);
					if (buffer!=NULL) free(buffer);
					return retval;
				}
				break;
			}

			break;
	}

	/* NOTE: don't forget to free(buffer) if you return early! */
	if (buffer!=NULL) free(buffer);
	return TRUE;
}

/*
 * rfbProcessClientNormalMessage is called when the client has sent a normal
 * protocol message.
 */

static void
rfbProcessClientNormalMessage(rfbClientPtr cl)
{
	int n=0;
	rfbClientToServerMsg msg;
	char *str;
	int i;
	uint32_t enc=0;
	uint32_t lastPreferredEncoding = -1;
	char encBuf[64];
	char encBuf2[64];

	if ((n = rfbReadExact(cl, (char *)&msg, 1)) <= 0) {
		if (n != 0)
			rfbLogPerror("rfbProcessClientNormalMessage: read");
		rfbCloseClient(cl);
		return;
	}

	switch (msg.type) {

	case rfbSetPixelFormat:

		if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
				sz_rfbSetPixelFormatMsg - 1)) <= 0) {
			if (n != 0)
				rfbLogPerror("rfbProcessClientNormalMessage: read");
			rfbCloseClient(cl);
			return;
		}

		cl->format.bitsPerPixel = msg.spf.format.bitsPerPixel;
		cl->format.depth = msg.spf.format.depth;
		cl->format.bigEndian = (msg.spf.format.bigEndian ? TRUE : FALSE);
		cl->format.trueColour = (msg.spf.format.trueColour ? TRUE : FALSE);
		cl->format.redMax = Swap16IfLE(msg.spf.format.redMax);
		cl->format.greenMax = Swap16IfLE(msg.spf.format.greenMax);
		cl->format.blueMax = Swap16IfLE(msg.spf.format.blueMax);
		cl->format.redShift = msg.spf.format.redShift;
		cl->format.greenShift = msg.spf.format.greenShift;
		cl->format.blueShift = msg.spf.format.blueShift;

		cl->readyForSetColourMapEntries = TRUE;
		cl->screen->setTranslateFunction(cl);

		rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbSetPixelFormatMsg, sz_rfbSetPixelFormatMsg);

		return;


	case rfbFixColourMapEntries:
		if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
				sz_rfbFixColourMapEntriesMsg - 1)) <= 0) {
			if (n != 0)
				rfbLogPerror("rfbProcessClientNormalMessage: read");
			rfbCloseClient(cl);
			return;
		}
		rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbSetPixelFormatMsg, sz_rfbSetPixelFormatMsg);
		rfbLog("rfbProcessClientNormalMessage: %s",
				"FixColourMapEntries unsupported\n");
		rfbCloseClient(cl);
		return;


		/* NOTE: Some clients send us a set of encodings (ie: PointerPos) designed to enable/disable features...
		 * We may want to look into this...
		 * Example:
		 *     case rfbEncodingXCursor:
		 *         cl->enableCursorShapeUpdates = TRUE;
		 *
		 * Currently: cl->enableCursorShapeUpdates can *never* be turned off...
		 */
	case rfbSetEncodings:
	{

		if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
				sz_rfbSetEncodingsMsg - 1)) <= 0) {
			if (n != 0)
				rfbLogPerror("rfbProcessClientNormalMessage: read");
			rfbCloseClient(cl);
			return;
		}

		msg.se.nEncodings = Swap16IfLE(msg.se.nEncodings);

		rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbSetEncodingsMsg+(msg.se.nEncodings*4),sz_rfbSetEncodingsMsg+(msg.se.nEncodings*4));

		/*
		 * UltraVNC Client has the ability to adapt to changing network environments
		 * So, let's give it a change to tell us what it wants now!
		 */
		if (cl->preferredEncoding!=-1)
			lastPreferredEncoding = cl->preferredEncoding;

		/* Reset all flags to defaults (allows us to switch between PointerPos and Server Drawn Cursors) */
		cl->preferredEncoding=-1;
		cl->useCopyRect              = FALSE;
		cl->useNewFBSize             = FALSE;
		cl->cursorWasChanged         = FALSE;
		cl->useRichCursorEncoding    = FALSE;
		cl->enableCursorPosUpdates   = FALSE;
		cl->enableCursorShapeUpdates = FALSE;
		cl->enableCursorShapeUpdates = FALSE;
		cl->enableLastRectEncoding   = FALSE;
		cl->enableKeyboardLedState   = FALSE;
		cl->enableSupportedMessages  = FALSE;
		cl->enableSupportedEncodings = FALSE;
		cl->enableServerIdentity     = FALSE;


		for (i = 0; i < msg.se.nEncodings; i++) {
			if ((n = rfbReadExact(cl, (char *)&enc, 4)) <= 0) {
				if (n != 0)
					rfbLogPerror("rfbProcessClientNormalMessage: read");
				rfbCloseClient(cl);
				return;
			}
			enc = Swap32IfLE(enc);

			switch (enc) {

			case rfbEncodingCopyRect:
				cl->useCopyRect = TRUE;
				break;
			case rfbEncodingRaw:
			case rfbEncodingRRE:
			case rfbEncodingCoRRE:
			case rfbEncodingHextile:
			case rfbEncodingUltra:
#ifdef LIBVNCSERVER_HAVE_LIBZ
			case rfbEncodingZlib:
			case rfbEncodingZRLE:
			case rfbEncodingZYWRLE:
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
			case rfbEncodingTight:
#endif
#endif
				/* The first supported encoding is the 'preferred' encoding */
				if (cl->preferredEncoding == -1)
					cl->preferredEncoding = enc;


				break;
			case rfbEncodingXCursor:
				if(!cl->screen->dontConvertRichCursorToXCursor) {
					rfbLog("Enabling X-style cursor updates for client %s\n",
							cl->host);
					/* if cursor was drawn, hide the cursor */
					if(!cl->enableCursorShapeUpdates)
						rfbRedrawAfterHideCursor(cl,NULL);

					cl->enableCursorShapeUpdates = TRUE;
					cl->cursorWasChanged = TRUE;
				}
				break;
			case rfbEncodingRichCursor:
				rfbLog("Enabling full-color cursor updates for client %s\n",
						cl->host);
				/* if cursor was drawn, hide the cursor */
				if(!cl->enableCursorShapeUpdates)
					rfbRedrawAfterHideCursor(cl,NULL);

				cl->enableCursorShapeUpdates = TRUE;
				cl->useRichCursorEncoding = TRUE;
				cl->cursorWasChanged = TRUE;
				break;
			case rfbEncodingPointerPos:
				if (!cl->enableCursorPosUpdates) {
					rfbLog("Enabling cursor position updates for client %s\n",
							cl->host);
					cl->enableCursorPosUpdates = TRUE;
					cl->cursorWasMoved = TRUE;
				}
				break;
			case rfbEncodingLastRect:
				if (!cl->enableLastRectEncoding) {
					rfbLog("Enabling LastRect protocol extension for client "
							"%s\n", cl->host);
					cl->enableLastRectEncoding = TRUE;
				}
				break;
			case rfbEncodingNewFBSize:
				if (!cl->useNewFBSize) {
					rfbLog("Enabling NewFBSize protocol extension for client "
							"%s\n", cl->host);
					cl->useNewFBSize = TRUE;
				}
				break;
			case rfbEncodingKeyboardLedState:
				if (!cl->enableKeyboardLedState) {
					rfbLog("Enabling KeyboardLedState protocol extension for client "
							"%s\n", cl->host);
					cl->enableKeyboardLedState = TRUE;
				}
				break;
			case rfbEncodingSupportedMessages:
				if (!cl->enableSupportedMessages) {
					rfbLog("Enabling SupportedMessages protocol extension for client "
							"%s\n", cl->host);
					cl->enableSupportedMessages = TRUE;
				}
				break;
			case rfbEncodingSupportedEncodings:
				if (!cl->enableSupportedEncodings) {
					rfbLog("Enabling SupportedEncodings protocol extension for client "
							"%s\n", cl->host);
					cl->enableSupportedEncodings = TRUE;
				}
				break;
			case rfbEncodingServerIdentity:
				if (!cl->enableServerIdentity) {
					rfbLog("Enabling ServerIdentity protocol extension for client "
							"%s\n", cl->host);
					cl->enableServerIdentity = TRUE;
				}
				break;
			default:
#ifdef LIBVNCSERVER_HAVE_LIBZ
				if ( enc >= (uint32_t)rfbEncodingCompressLevel0 &&
						enc <= (uint32_t)rfbEncodingCompressLevel9 ) {
					cl->zlibCompressLevel = enc & 0x0F;
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
					cl->tightCompressLevel = enc & 0x0F;
					rfbLog("Using compression level %d for client %s\n",
							cl->tightCompressLevel, cl->host);
#endif
				} else if ( enc >= (uint32_t)rfbEncodingQualityLevel0 &&
						enc <= (uint32_t)rfbEncodingQualityLevel9 ) {
					cl->tightQualityLevel = enc & 0x0F;
					rfbLog("Using image quality level %d for client %s\n",
							cl->tightQualityLevel, cl->host);
				} else
#endif
				{
					rfbExtensionData* e;
					for(e = cl->extensions; e;) {
						rfbExtensionData* next = e->next;
						if(e->extension->enablePseudoEncoding &&
								e->extension->enablePseudoEncoding(cl,
										&e->data, (int)enc))
							/* ext handles this encoding */
							break;
						e = next;
					}
					if(e == NULL) {
						rfbBool handled = FALSE;
						/* if the pseudo encoding is not handled by the
				   enabled extensions, search through all
				   extensions. */
						rfbProtocolExtension* e;

						for(e = rfbGetExtensionIterator(); e;) {
							int* encs = e->pseudoEncodings;
							while(encs && *encs!=0) {
								if(*encs==(int)enc) {
									void* data = NULL;
									if(!e->enablePseudoEncoding(cl, &data, (int)enc)) {
										rfbLog("Installed extension pretends to handle pseudo encoding 0x%x, but does not!\n",(int)enc);
									} else {
										rfbEnableExtension(cl, e, data);
										handled = TRUE;
										e = NULL;
										break;
									}
								}
								encs++;
							}

							if(e)
								e = e->next;
						}
						rfbReleaseExtensionIterator();

						if(!handled)
							rfbLog("rfbProcessClientNormalMessage: "
									"ignoring unsupported encoding type %s\n",
									encodingName(enc,encBuf,sizeof(encBuf)));
					}
				}
			}
		}



		if (cl->preferredEncoding == -1) {
			if ((int32_t)lastPreferredEncoding==-1) {
				cl->preferredEncoding = rfbEncodingRaw;
				rfbLog("Defaulting to %s encoding for client %s\n", encodingName(cl->preferredEncoding,encBuf,sizeof(encBuf)),cl->host);
			}
			else {
				cl->preferredEncoding = lastPreferredEncoding;
				rfbLog("Sticking with %s encoding for client %s\n", encodingName(cl->preferredEncoding,encBuf,sizeof(encBuf)),cl->host);
			}
		}
		else
		{
			if ((int32_t)lastPreferredEncoding==-1) {
				rfbLog("Using %s encoding for client %s\n", encodingName(cl->preferredEncoding,encBuf,sizeof(encBuf)),cl->host);
			} else {
				rfbLog("Switching from %s to %s Encoding for client %s\n",
						encodingName(lastPreferredEncoding,encBuf2,sizeof(encBuf2)),
						encodingName(cl->preferredEncoding,encBuf,sizeof(encBuf)), cl->host);
			}
		}

		if (cl->enableCursorPosUpdates && !cl->enableCursorShapeUpdates) {
			rfbLog("Disabling cursor position updates for client %s\n",
					cl->host);
			cl->enableCursorPosUpdates = FALSE;
		}

		return;
	}


	case rfbFramebufferUpdateRequest:
	{
		sraRegionPtr tmpRegion;

		if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
				sz_rfbFramebufferUpdateRequestMsg-1)) <= 0) {
			if (n != 0)
				rfbLogPerror("rfbProcessClientNormalMessage: read");
			rfbCloseClient(cl);
			return;
		}

		rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbFramebufferUpdateRequestMsg,sz_rfbFramebufferUpdateRequestMsg);

		/* The values come in based on the scaled screen, we need to convert them to
		 * values based on the main screen's coordinate system
		 */
		if(!rectSwapIfLEAndClip(&msg.fur.x,&msg.fur.y,&msg.fur.w,&msg.fur.h,cl))
		{
			rfbLog("Warning, ignoring rfbFramebufferUpdateRequest: %dXx%dY-%dWx%dH\n",msg.fur.x, msg.fur.y, msg.fur.w, msg.fur.h);
			return;
		}


		tmpRegion =
				sraRgnCreateRect(msg.fur.x,
						msg.fur.y,
						msg.fur.x+msg.fur.w,
						msg.fur.y+msg.fur.h);

		LOCK(cl->updateMutex);
		sraRgnOr(cl->requestedRegion,tmpRegion);

		if (!cl->readyForSetColourMapEntries) {
			/* client hasn't sent a SetPixelFormat so is using server's */
			cl->readyForSetColourMapEntries = TRUE;
			if (!cl->format.trueColour) {
				if (!rfbSetClientColourMap(cl, 0, 0)) {
					sraRgnDestroy(tmpRegion);
					UNLOCK(cl->updateMutex);
					return;
				}
			}
		}

		if (!msg.fur.incremental) {
			sraRgnOr(cl->modifiedRegion,tmpRegion);
			sraRgnSubtract(cl->copyRegion,tmpRegion);
		}
		TSIGNAL(cl->updateCond);
		UNLOCK(cl->updateMutex);

		sraRgnDestroy(tmpRegion);

		return;
	}

	case rfbKeyEvent:

		if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
				sz_rfbKeyEventMsg - 1)) <= 0) {
			if (n != 0)
				rfbLogPerror("rfbProcessClientNormalMessage: read");
			rfbCloseClient(cl);
			return;
		}

		rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbKeyEventMsg, sz_rfbKeyEventMsg);

		if(!cl->viewOnly) {
			cl->screen->kbdAddEvent(msg.ke.down, (rfbKeySym)Swap32IfLE(msg.ke.key), cl);
		}

		return;


	case rfbPointerEvent:

		if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
				sz_rfbPointerEventMsg - 1)) <= 0) {
			if (n != 0)
				rfbLogPerror("rfbProcessClientNormalMessage: read");
			rfbCloseClient(cl);
			return;
		}

		rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbPointerEventMsg, sz_rfbPointerEventMsg);

		if (cl->screen->pointerClient && cl->screen->pointerClient != cl)
			return;

		if (msg.pe.buttonMask == 0)
			cl->screen->pointerClient = NULL;
		else
			cl->screen->pointerClient = cl;

		if(!cl->viewOnly) {
			if (msg.pe.buttonMask != cl->lastPtrButtons ||
					cl->screen->deferPtrUpdateTime == 0) {
				cl->screen->ptrAddEvent(msg.pe.buttonMask,
						ScaleX(cl->scaledScreen, cl->screen, Swap16IfLE(msg.pe.x)),
						ScaleY(cl->scaledScreen, cl->screen, Swap16IfLE(msg.pe.y)),
						cl);
				cl->lastPtrButtons = msg.pe.buttonMask;
			} else {
				cl->lastPtrX = ScaleX(cl->scaledScreen, cl->screen, Swap16IfLE(msg.pe.x));
				cl->lastPtrY = ScaleY(cl->scaledScreen, cl->screen, Swap16IfLE(msg.pe.y));
				cl->lastPtrButtons = msg.pe.buttonMask;
			}
		}
		return;


	case rfbFileTransfer:
		if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
				sz_rfbFileTransferMsg - 1)) <= 0) {
			if (n != 0)
				rfbLogPerror("rfbProcessClientNormalMessage: read");
			rfbCloseClient(cl);
			return;
		}
		msg.ft.size         = Swap32IfLE(msg.ft.size);
		msg.ft.length       = Swap32IfLE(msg.ft.length);
		/* record statistics in rfbProcessFileTransfer as length is filled with garbage when it is not valid */
		rfbProcessFileTransfer(cl, msg.ft.contentType, msg.ft.contentParam, msg.ft.size, msg.ft.length);
		return;

	case rfbSetSW:
		if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
				sz_rfbSetSWMsg - 1)) <= 0) {
			if (n != 0)
				rfbLogPerror("rfbProcessClientNormalMessage: read");
			rfbCloseClient(cl);
			return;
		}
		msg.sw.x = Swap16IfLE(msg.sw.x);
		msg.sw.y = Swap16IfLE(msg.sw.y);
		rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbSetSWMsg, sz_rfbSetSWMsg);
		/* msg.sw.status is not initialized in the ultraVNC viewer and contains random numbers (why???) */

		rfbLog("Received a rfbSetSingleWindow(%d x, %d y)\n", msg.sw.x, msg.sw.y);
		if (cl->screen->setSingleWindow!=NULL)
			cl->screen->setSingleWindow(cl, msg.sw.x, msg.sw.y);
		return;

	case rfbSetServerInput:
		if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
				sz_rfbSetServerInputMsg - 1)) <= 0) {
			if (n != 0)
				rfbLogPerror("rfbProcessClientNormalMessage: read");
			rfbCloseClient(cl);
			return;
		}
		rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbSetServerInputMsg, sz_rfbSetServerInputMsg);

		/* msg.sim.pad is not initialized in the ultraVNC viewer and contains random numbers (why???) */
		/* msg.sim.pad = Swap16IfLE(msg.sim.pad); */

		rfbLog("Received a rfbSetServerInput(%d status)\n", msg.sim.status);
		if (cl->screen->setServerInput!=NULL)
			cl->screen->setServerInput(cl, msg.sim.status);
		return;

	case rfbTextChat:
		if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
				sz_rfbTextChatMsg - 1)) <= 0) {
			if (n != 0)
				rfbLogPerror("rfbProcessClientNormalMessage: read");
			rfbCloseClient(cl);
			return;
		}

		msg.tc.pad2   = Swap16IfLE(msg.tc.pad2);
		msg.tc.length = Swap32IfLE(msg.tc.length);

		switch (msg.tc.length) {
		case rfbTextChatOpen:
		case rfbTextChatClose:
		case rfbTextChatFinished:
			/* commands do not have text following */
			/* Why couldn't they have used the pad byte??? */
			str=NULL;
			rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbTextChatMsg, sz_rfbTextChatMsg);
			break;
		default:
			if ((msg.tc.length>0) && (msg.tc.length<rfbTextMaxSize))
			{
				str = (char *)malloc(msg.tc.length);
				if (str==NULL)
				{
					rfbLog("Unable to malloc %d bytes for a TextChat Message\n", msg.tc.length);
					rfbCloseClient(cl);
					return;
				}
				if ((n = rfbReadExact(cl, str, msg.tc.length)) <= 0) {
					if (n != 0)
						rfbLogPerror("rfbProcessClientNormalMessage: read");
					free(str);
					rfbCloseClient(cl);
					return;
				}
				rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbTextChatMsg+msg.tc.length, sz_rfbTextChatMsg+msg.tc.length);
			}
			else
			{
				/* This should never happen */
				rfbLog("client sent us a Text Message that is too big %d>%d\n", msg.tc.length, rfbTextMaxSize);
				rfbCloseClient(cl);
				return;
			}
		}

		/* Note: length can be commands: rfbTextChatOpen, rfbTextChatClose, and rfbTextChatFinished
		 * at which point, the str is NULL (as it is not sent)
		 */
		if (cl->screen->setTextChat!=NULL)
			cl->screen->setTextChat(cl, msg.tc.length, str);

		free(str);
		return;


		case rfbClientCutText:

			if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
					sz_rfbClientCutTextMsg - 1)) <= 0) {
				if (n != 0)
					rfbLogPerror("rfbProcessClientNormalMessage: read");
				rfbCloseClient(cl);
				return;
			}

			msg.cct.length = Swap32IfLE(msg.cct.length);

			str = (char *)malloc(msg.cct.length);

			if ((n = rfbReadExact(cl, str, msg.cct.length)) <= 0) {
				if (n != 0)
					rfbLogPerror("rfbProcessClientNormalMessage: read");
				free(str);
				rfbCloseClient(cl);
				return;
			}
			rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbClientCutTextMsg+msg.cct.length, sz_rfbClientCutTextMsg+msg.cct.length);
			if(!cl->viewOnly) {
				cl->screen->setXCutText(str, msg.cct.length, cl);
			}
			free(str);

			return;

		case rfbPalmVNCSetScaleFactor:
			cl->PalmVNC = TRUE;
			if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
					sz_rfbSetScaleMsg - 1)) <= 0) {
				if (n != 0)
					rfbLogPerror("rfbProcessClientNormalMessage: read");
				rfbCloseClient(cl);
				return;
			}
			rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbSetScaleMsg, sz_rfbSetScaleMsg);
			rfbLog("rfbSetScale(%d)\n", msg.ssc.scale);
			rfbScalingSetup(cl,cl->screen->width/msg.ssc.scale, cl->screen->height/msg.ssc.scale);

			rfbSendNewScaleSize(cl);
			return;

		case rfbSetScale:

			if ((n = rfbReadExact(cl, ((char *)&msg) + 1,
					sz_rfbSetScaleMsg - 1)) <= 0) {
				if (n != 0)
					rfbLogPerror("rfbProcessClientNormalMessage: read");
				rfbCloseClient(cl);
				return;
			}
			rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbSetScaleMsg, sz_rfbSetScaleMsg);
			rfbLog("rfbSetScale(%d)\n", msg.ssc.scale);
			rfbScalingSetup(cl,cl->screen->width/msg.ssc.scale, cl->screen->height/msg.ssc.scale);

			rfbSendNewScaleSize(cl);
			return;

		default:
		{
			rfbExtensionData *e,*next;

			for(e=cl->extensions; e;) {
				next = e->next;
				if(e->extension->handleMessage &&
						e->extension->handleMessage(cl, e->data, &msg))
				{
					rfbStatRecordMessageRcvd(cl, msg.type, 0, 0); /* Extension should handle this */
					return;
				}
				e = next;
			}

			rfbLog("rfbProcessClientNormalMessage: unknown message type %d\n",
					msg.type);
			rfbLog(" ... closing connection\n");
			rfbCloseClient(cl);
			return;
		}
	}
}



/*
 * rfbSendFramebufferUpdate - send the currently pending framebuffer update to
 * the RFB client.
 * givenUpdateRegion is not changed.
 */

rfbBool
rfbSendFramebufferUpdate(rfbClientPtr cl,
		sraRegionPtr givenUpdateRegion)
{
	sraRectangleIterator* i=NULL;
	sraRect rect;
	int nUpdateRegionRects;
	rfbFramebufferUpdateMsg *fu = (rfbFramebufferUpdateMsg *)cl->updateBuf;
	sraRegionPtr updateRegion,updateCopyRegion,tmpRegion;
	int dx, dy;
	rfbBool sendCursorShape = FALSE;
	rfbBool sendCursorPos = FALSE;
	rfbBool sendKeyboardLedState = FALSE;
	rfbBool sendSupportedMessages = FALSE;
	rfbBool sendSupportedEncodings = FALSE;
	rfbBool sendServerIdentity = FALSE;
	rfbBool result = TRUE;

	if(cl->screen->displayHook)
		cl->screen->displayHook(cl);

	/*
	 * If framebuffer size was changed and the client supports NewFBSize
	 * encoding, just send NewFBSize marker and return.
	 */

	if (cl->useNewFBSize && cl->newFBSizePending) {
		LOCK(cl->updateMutex);
		cl->newFBSizePending = FALSE;
		UNLOCK(cl->updateMutex);
		fu->type = rfbFramebufferUpdate;
		fu->nRects = Swap16IfLE(1);
		cl->ublen = sz_rfbFramebufferUpdateMsg;
		if (!rfbSendNewFBSize(cl, cl->scaledScreen->width, cl->scaledScreen->height)) {
			return FALSE;
		}
		return rfbSendUpdateBuf(cl);
	}

	/*
	 * If this client understands cursor shape updates, cursor should be
	 * removed from the framebuffer. Otherwise, make sure it's put up.
	 */

	if (cl->enableCursorShapeUpdates) {
		if (cl->cursorWasChanged && cl->readyForSetColourMapEntries)
			sendCursorShape = TRUE;
	}

	/*
	 * Do we plan to send cursor position update?
	 */

	if (cl->enableCursorPosUpdates && cl->cursorWasMoved)
		sendCursorPos = TRUE;

	/*
	 * Do we plan to send a keyboard state update?
	 */
	if ((cl->enableKeyboardLedState) &&
			(cl->screen->getKeyboardLedStateHook!=NULL))
	{
		int x;
		x=cl->screen->getKeyboardLedStateHook(cl->screen);
		if (x!=cl->lastKeyboardLedState)
		{
			sendKeyboardLedState = TRUE;
			cl->lastKeyboardLedState=x;
		}
	}

	/*
	 * Do we plan to send a rfbEncodingSupportedMessages?
	 */
	if (cl->enableSupportedMessages)
	{
		sendSupportedMessages = TRUE;
		/* We only send this message ONCE <per setEncodings message received>
		 * (We disable it here)
		 */
		cl->enableSupportedMessages = FALSE;
	}
	/*
	 * Do we plan to send a rfbEncodingSupportedEncodings?
	 */
	if (cl->enableSupportedEncodings)
	{
		sendSupportedEncodings = TRUE;
		/* We only send this message ONCE <per setEncodings message received>
		 * (We disable it here)
		 */
		cl->enableSupportedEncodings = FALSE;
	}
	/*
	 * Do we plan to send a rfbEncodingServerIdentity?
	 */
	if (cl->enableServerIdentity)
	{
		sendServerIdentity = TRUE;
		/* We only send this message ONCE <per setEncodings message received>
		 * (We disable it here)
		 */
		cl->enableServerIdentity = FALSE;
	}

	LOCK(cl->updateMutex);

	/*
	 * The modifiedRegion may overlap the destination copyRegion.  We remove
	 * any overlapping bits from the copyRegion (since they'd only be
	 * overwritten anyway).
	 */

	sraRgnSubtract(cl->copyRegion,cl->modifiedRegion);

	/*
	 * The client is interested in the region requestedRegion.  The region
	 * which should be updated now is the intersection of requestedRegion
	 * and the union of modifiedRegion and copyRegion.  If it's empty then
	 * no update is needed.
	 */

	updateRegion = sraRgnCreateRgn(givenUpdateRegion);
	if(cl->screen->progressiveSliceHeight>0) {
		int height=cl->screen->progressiveSliceHeight,
				y=cl->progressiveSliceY;
		sraRegionPtr bbox=sraRgnBBox(updateRegion);
		sraRect rect;
		if(sraRgnPopRect(bbox,&rect,0)) {
			sraRegionPtr slice;
			if(y<rect.y1 || y>=rect.y2)
				y=rect.y1;
			slice=sraRgnCreateRect(0,y,cl->screen->width,y+height);
			sraRgnAnd(updateRegion,slice);
			sraRgnDestroy(slice);
		}
		sraRgnDestroy(bbox);
		y+=height;
		if(y>=cl->screen->height)
			y=0;
		cl->progressiveSliceY=y;
	}

	sraRgnOr(updateRegion,cl->copyRegion);
	if(!sraRgnAnd(updateRegion,cl->requestedRegion) &&
			sraRgnEmpty(updateRegion) &&
			(cl->enableCursorShapeUpdates ||
					(cl->cursorX == cl->screen->cursorX && cl->cursorY == cl->screen->cursorY)) &&
					!sendCursorShape && !sendCursorPos && !sendKeyboardLedState &&
					!sendSupportedMessages && !sendSupportedEncodings && !sendServerIdentity) {
		sraRgnDestroy(updateRegion);
		UNLOCK(cl->updateMutex);
		return TRUE;
	}

	/*
	 * We assume that the client doesn't have any pixel data outside the
	 * requestedRegion.  In other words, both the source and destination of a
	 * copy must lie within requestedRegion.  So the region we can send as a
	 * copy is the intersection of the copyRegion with both the requestedRegion
	 * and the requestedRegion translated by the amount of the copy.  We set
	 * updateCopyRegion to this.
	 */

	updateCopyRegion = sraRgnCreateRgn(cl->copyRegion);
	sraRgnAnd(updateCopyRegion,cl->requestedRegion);
	tmpRegion = sraRgnCreateRgn(cl->requestedRegion);
	sraRgnOffset(tmpRegion,cl->copyDX,cl->copyDY);
	sraRgnAnd(updateCopyRegion,tmpRegion);
	sraRgnDestroy(tmpRegion);
	dx = cl->copyDX;
	dy = cl->copyDY;

	/*
	 * Next we remove updateCopyRegion from updateRegion so that updateRegion
	 * is the part of this update which is sent as ordinary pixel data (i.e not
	 * a copy).
	 */

	sraRgnSubtract(updateRegion,updateCopyRegion);

	/*
	 * Finally we leave modifiedRegion to be the remainder (if any) of parts of
	 * the screen which are modified but outside the requestedRegion.  We also
	 * empty both the requestedRegion and the copyRegion - note that we never
	 * carry over a copyRegion for a future update.
	 */

	sraRgnOr(cl->modifiedRegion,cl->copyRegion);
	sraRgnSubtract(cl->modifiedRegion,updateRegion);
	sraRgnSubtract(cl->modifiedRegion,updateCopyRegion);

	sraRgnMakeEmpty(cl->requestedRegion);
	sraRgnMakeEmpty(cl->copyRegion);
	cl->copyDX = 0;
	cl->copyDY = 0;

	UNLOCK(cl->updateMutex);

	if (!cl->enableCursorShapeUpdates) {
		if(cl->cursorX != cl->screen->cursorX || cl->cursorY != cl->screen->cursorY) {
			rfbRedrawAfterHideCursor(cl,updateRegion);
			LOCK(cl->screen->cursorMutex);
			cl->cursorX = cl->screen->cursorX;
			cl->cursorY = cl->screen->cursorY;
			UNLOCK(cl->screen->cursorMutex);
			rfbRedrawAfterHideCursor(cl,updateRegion);
		}
		rfbShowCursor(cl);
	}

	/*
	 * Now send the update.
	 */

	rfbStatRecordMessageSent(cl, rfbFramebufferUpdate, 0, 0);
	if (cl->preferredEncoding == rfbEncodingCoRRE) {
		nUpdateRegionRects = 0;

		for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);){
			int x = rect.x1;
			int y = rect.y1;
			int w = rect.x2 - x;
			int h = rect.y2 - y;
			int rectsPerRow, rows;
			/* We need to count the number of rects in the scaled screen */
			if (cl->screen!=cl->scaledScreen)
				rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h, "rfbSendFramebufferUpdate");
			rectsPerRow = (w-1)/cl->correMaxWidth+1;
			rows = (h-1)/cl->correMaxHeight+1;
			nUpdateRegionRects += rectsPerRow*rows;
		}
		sraRgnReleaseIterator(i); i=NULL;
	} else if (cl->preferredEncoding == rfbEncodingUltra) {
		nUpdateRegionRects = 0;

		for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);){
			int x = rect.x1;
			int y = rect.y1;
			int w = rect.x2 - x;
			int h = rect.y2 - y;
			/* We need to count the number of rects in the scaled screen */
			if (cl->screen!=cl->scaledScreen)
				rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h, "rfbSendFramebufferUpdate");
			nUpdateRegionRects += (((h-1) / (ULTRA_MAX_SIZE( w ) / w)) + 1);
		}
		sraRgnReleaseIterator(i); i=NULL;
#ifdef LIBVNCSERVER_HAVE_LIBZ
	} else if (cl->preferredEncoding == rfbEncodingZlib) {
		nUpdateRegionRects = 0;

		for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);){
			int x = rect.x1;
			int y = rect.y1;
			int w = rect.x2 - x;
			int h = rect.y2 - y;
			/* We need to count the number of rects in the scaled screen */
			if (cl->screen!=cl->scaledScreen)
				rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h, "rfbSendFramebufferUpdate");
			nUpdateRegionRects += (((h-1) / (ZLIB_MAX_SIZE( w ) / w)) + 1);
		}
		sraRgnReleaseIterator(i); i=NULL;
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
	} else if (cl->preferredEncoding == rfbEncodingTight) {
		nUpdateRegionRects = 0;

		for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);){
			int x = rect.x1;
			int y = rect.y1;
			int w = rect.x2 - x;
			int h = rect.y2 - y;
			int n;
			/* We need to count the number of rects in the scaled screen */
			if (cl->screen!=cl->scaledScreen)
				rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h, "rfbSendFramebufferUpdate");
			n = rfbNumCodedRectsTight(cl, x, y, w, h);
			if (n == 0) {
				nUpdateRegionRects = 0xFFFF;
				break;
			}
			nUpdateRegionRects += n;
		}
		sraRgnReleaseIterator(i); i=NULL;
#endif
#endif
	} else {
		nUpdateRegionRects = sraRgnCountRects(updateRegion);
	}

	fu->type = rfbFramebufferUpdate;
	if (nUpdateRegionRects != 0xFFFF) {
		if(cl->screen->maxRectsPerUpdate>0
				/* CoRRE splits the screen into smaller squares */
				&& cl->preferredEncoding != rfbEncodingCoRRE
				/* Ultra encoding splits rectangles up into smaller chunks */
				&& cl->preferredEncoding != rfbEncodingUltra
#ifdef LIBVNCSERVER_HAVE_LIBZ
				/* Zlib encoding splits rectangles up into smaller chunks */
				&& cl->preferredEncoding != rfbEncodingZlib
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
				/* Tight encoding counts the rectangles differently */
				&& cl->preferredEncoding != rfbEncodingTight
#endif
#endif
				&& nUpdateRegionRects>cl->screen->maxRectsPerUpdate) {
			sraRegion* newUpdateRegion = sraRgnBBox(updateRegion);
			sraRgnDestroy(updateRegion);
			updateRegion = newUpdateRegion;
			nUpdateRegionRects = sraRgnCountRects(updateRegion);
		}
		fu->nRects = Swap16IfLE((uint16_t)(sraRgnCountRects(updateCopyRegion) +
				nUpdateRegionRects +
				!!sendCursorShape + !!sendCursorPos + !!sendKeyboardLedState +
				!!sendSupportedMessages + !!sendSupportedEncodings + !!sendServerIdentity));
	} else {
		fu->nRects = 0xFFFF;
	}
	cl->ublen = sz_rfbFramebufferUpdateMsg;

	if (sendCursorShape) {
		cl->cursorWasChanged = FALSE;
		if (!rfbSendCursorShape(cl))
			goto updateFailed;
	}

	if (sendCursorPos) {
		cl->cursorWasMoved = FALSE;
		if (!rfbSendCursorPos(cl))
			goto updateFailed;
	}

	if (sendKeyboardLedState) {
		if (!rfbSendKeyboardLedState(cl))
			goto updateFailed;
	}

	if (sendSupportedMessages) {
		if (!rfbSendSupportedMessages(cl))
			goto updateFailed;
	}
	if (sendSupportedEncodings) {
		if (!rfbSendSupportedEncodings(cl))
			goto updateFailed;
	}
	if (sendServerIdentity) {
		if (!rfbSendServerIdentity(cl))
			goto updateFailed;
	}

	if (!sraRgnEmpty(updateCopyRegion)) {
		if (!rfbSendCopyRegion(cl,updateCopyRegion,dx,dy))
			goto updateFailed;
	}

	for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);){
		int x = rect.x1;
		int y = rect.y1;
		int w = rect.x2 - x;
		int h = rect.y2 - y;

		/* We need to count the number of rects in the scaled screen */
		if (cl->screen!=cl->scaledScreen)
			rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h, "rfbSendFramebufferUpdate");

		switch (cl->preferredEncoding) {
		case -1:
		case rfbEncodingRaw:
			if (!rfbSendRectEncodingRaw(cl, x, y, w, h))
				goto updateFailed;
			break;
		case rfbEncodingRRE:
			if (!rfbSendRectEncodingRRE(cl, x, y, w, h))
				goto updateFailed;
			break;
		case rfbEncodingCoRRE:
			if (!rfbSendRectEncodingCoRRE(cl, x, y, w, h))
				goto updateFailed;
			break;
		case rfbEncodingHextile:
			if (!rfbSendRectEncodingHextile(cl, x, y, w, h))
				goto updateFailed;
			break;
		case rfbEncodingUltra:
			if (!rfbSendRectEncodingUltra(cl, x, y, w, h))
				goto updateFailed;
			break;
#ifdef LIBVNCSERVER_HAVE_LIBZ
		case rfbEncodingZlib:
			if (!rfbSendRectEncodingZlib(cl, x, y, w, h))
				goto updateFailed;
			break;
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
		case rfbEncodingTight:
			if (!rfbSendRectEncodingTight(cl, x, y, w, h))
				goto updateFailed;
			break;
#endif
#endif
#ifdef LIBVNCSERVER_HAVE_LIBZ
		case rfbEncodingZRLE:
		case rfbEncodingZYWRLE:
			if (!rfbSendRectEncodingZRLE(cl, x, y, w, h))
				goto updateFailed;
			break;
#endif
		}
	}
	if (i) {
		sraRgnReleaseIterator(i);
		i = NULL;
	}

	if ( nUpdateRegionRects == 0xFFFF &&
			!rfbSendLastRectMarker(cl) )
		goto updateFailed;

	if (!rfbSendUpdateBuf(cl)) {
		updateFailed:
		result = FALSE;
	}

	if (!cl->enableCursorShapeUpdates) {
		rfbHideCursor(cl);
	}

	if(i)
		sraRgnReleaseIterator(i);
	sraRgnDestroy(updateRegion);
	sraRgnDestroy(updateCopyRegion);
	return result;
}


/*
 * Send the copy region as a string of CopyRect encoded rectangles.
 * The only slightly tricky thing is that we should send the messages in
 * the correct order so that an earlier CopyRect will not corrupt the source
 * of a later one.
 */

rfbBool
rfbSendCopyRegion(rfbClientPtr cl,
		sraRegionPtr reg,
		int dx,
		int dy)
{
	int x, y, w, h;
	rfbFramebufferUpdateRectHeader rect;
	rfbCopyRect cr;
	sraRectangleIterator* i;
	sraRect rect1;

	/* printf("copyrect: "); sraRgnPrint(reg); putchar('\n');fflush(stdout); */
	i = sraRgnGetReverseIterator(reg,dx>0,dy>0);

	/* correct for the scale of the screen */
	dx = ScaleX(cl->screen, cl->scaledScreen, dx);
	dy = ScaleX(cl->screen, cl->scaledScreen, dy);

	while(sraRgnIteratorNext(i,&rect1)) {
		x = rect1.x1;
		y = rect1.y1;
		w = rect1.x2 - x;
		h = rect1.y2 - y;

		/* correct for scaling (if necessary) */
		rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h, "copyrect");

		rect.r.x = Swap16IfLE(x);
		rect.r.y = Swap16IfLE(y);
		rect.r.w = Swap16IfLE(w);
		rect.r.h = Swap16IfLE(h);
		rect.encoding = Swap32IfLE(rfbEncodingCopyRect);

		memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
				sz_rfbFramebufferUpdateRectHeader);
		cl->ublen += sz_rfbFramebufferUpdateRectHeader;

		cr.srcX = Swap16IfLE(x - dx);
		cr.srcY = Swap16IfLE(y - dy);

		memcpy(&cl->updateBuf[cl->ublen], (char *)&cr, sz_rfbCopyRect);
		cl->ublen += sz_rfbCopyRect;

		rfbStatRecordEncodingSent(cl, rfbEncodingCopyRect, sz_rfbFramebufferUpdateRectHeader + sz_rfbCopyRect,
				w * h  * (cl->scaledScreen->bitsPerPixel / 8));
	}
	sraRgnReleaseIterator(i);

	return TRUE;
}

/*
 * Send a given rectangle in raw encoding (rfbEncodingRaw).
 */

rfbBool
rfbSendRectEncodingRaw(rfbClientPtr cl,
		int x,
		int y,
		int w,
		int h)
{
	rfbFramebufferUpdateRectHeader rect;
	int nlines;
	int bytesPerLine = w * (cl->format.bitsPerPixel / 8);
	char *fbptr = (cl->scaledScreen->frameBuffer + (cl->scaledScreen->paddedWidthInBytes * y)
			+ (x * (cl->scaledScreen->bitsPerPixel / 8)));

	/* Flush the buffer to guarantee correct alignment for translateFn(). */
	if (cl->ublen > 0) {
		if (!rfbSendUpdateBuf(cl))
			return FALSE;
	}

	rect.r.x = Swap16IfLE(x);
	rect.r.y = Swap16IfLE(y);
	rect.r.w = Swap16IfLE(w);
	rect.r.h = Swap16IfLE(h);
	rect.encoding = Swap32IfLE(rfbEncodingRaw);

	memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,sz_rfbFramebufferUpdateRectHeader);
	cl->ublen += sz_rfbFramebufferUpdateRectHeader;


	rfbStatRecordEncodingSent(cl, rfbEncodingRaw, sz_rfbFramebufferUpdateRectHeader + bytesPerLine * h,
			sz_rfbFramebufferUpdateRectHeader + bytesPerLine * h);

	nlines = (UPDATE_BUF_SIZE - cl->ublen) / bytesPerLine;

	while (TRUE) {
		if (nlines > h)
			nlines = h;

		(*cl->translateFn)(cl->translateLookupTable,
				&(cl->screen->serverFormat),
				&cl->format, fbptr, &cl->updateBuf[cl->ublen],
				cl->scaledScreen->paddedWidthInBytes, w, nlines);

		cl->ublen += nlines * bytesPerLine;
		h -= nlines;

		if (h == 0)     /* rect fitted in buffer, do next one */
			return TRUE;

		/* buffer full - flush partial rect and do another nlines */

		if (!rfbSendUpdateBuf(cl))
			return FALSE;

		fbptr += (cl->scaledScreen->paddedWidthInBytes * nlines);

		nlines = (UPDATE_BUF_SIZE - cl->ublen) / bytesPerLine;
		if (nlines == 0) {
			rfbErr("rfbSendRectEncodingRaw: send buffer too small for %d "
					"bytes per line\n", bytesPerLine);
			rfbCloseClient(cl);
			return FALSE;
		}
	}
}



/*
 * Send an empty rectangle with encoding field set to value of
 * rfbEncodingLastRect to notify client that this is the last
 * rectangle in framebuffer update ("LastRect" extension of RFB
 * protocol).
 */

rfbBool
rfbSendLastRectMarker(rfbClientPtr cl)
{
	rfbFramebufferUpdateRectHeader rect;

	if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) {
		if (!rfbSendUpdateBuf(cl))
			return FALSE;
	}

	rect.encoding = Swap32IfLE(rfbEncodingLastRect);
	rect.r.x = 0;
	rect.r.y = 0;
	rect.r.w = 0;
	rect.r.h = 0;

	memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,sz_rfbFramebufferUpdateRectHeader);
	cl->ublen += sz_rfbFramebufferUpdateRectHeader;


	rfbStatRecordEncodingSent(cl, rfbEncodingLastRect, sz_rfbFramebufferUpdateRectHeader, sz_rfbFramebufferUpdateRectHeader);

	return TRUE;
}


/*
 * Send NewFBSize pseudo-rectangle. This tells the client to change
 * its framebuffer size.
 */

rfbBool
rfbSendNewFBSize(rfbClientPtr cl,
		int w,
		int h)
{
	rfbFramebufferUpdateRectHeader rect;

	if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) {
		if (!rfbSendUpdateBuf(cl))
			return FALSE;
	}

	if (cl->PalmVNC==TRUE)
		rfbLog("Sending rfbEncodingNewFBSize in response to a PalmVNC style framebuffer resize (%dx%d)\n", w, h);
	else
		rfbLog("Sending rfbEncodingNewFBSize for resize to (%dx%d)\n", w, h);

	rect.encoding = Swap32IfLE(rfbEncodingNewFBSize);
	rect.r.x = 0;
	rect.r.y = 0;
	rect.r.w = Swap16IfLE(w);
	rect.r.h = Swap16IfLE(h);

	memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
			sz_rfbFramebufferUpdateRectHeader);
	cl->ublen += sz_rfbFramebufferUpdateRectHeader;

	rfbStatRecordEncodingSent(cl, rfbEncodingNewFBSize, sz_rfbFramebufferUpdateRectHeader, sz_rfbFramebufferUpdateRectHeader);

	return TRUE;
}


/*
 * Send the contents of cl->updateBuf.  Returns 1 if successful, -1 if
 * not (errno should be set).
 */

rfbBool
rfbSendUpdateBuf(rfbClientPtr cl)
{
	if(cl->sock<0)
		return FALSE;

	if (rfbWriteExact(cl, cl->updateBuf, cl->ublen) < 0) {
		rfbLogPerror("rfbSendUpdateBuf: write");
		rfbCloseClient(cl);
		return FALSE;
	}

	cl->ublen = 0;
	return TRUE;
}

/*
 * rfbSendSetColourMapEntries sends a SetColourMapEntries message to the
 * client, using values from the currently installed colormap.
 */

rfbBool
rfbSendSetColourMapEntries(rfbClientPtr cl,
		int firstColour,
		int nColours)
{
	char buf[sz_rfbSetColourMapEntriesMsg + 256 * 3 * 2];
	char *wbuf = buf;
	rfbSetColourMapEntriesMsg *scme;
	uint16_t *rgb;
	rfbColourMap* cm = &cl->screen->colourMap;
	int i, len;

	if (nColours > 256) {
		/* some rare hardware has, e.g., 4096 colors cells: PseudoColor:12 */
		wbuf = (char *) malloc(sz_rfbSetColourMapEntriesMsg + nColours * 3 * 2);
	}

	scme = (rfbSetColourMapEntriesMsg *)wbuf;
	rgb = (uint16_t *)(&wbuf[sz_rfbSetColourMapEntriesMsg]);

	scme->type = rfbSetColourMapEntries;

	scme->firstColour = Swap16IfLE(firstColour);
	scme->nColours = Swap16IfLE(nColours);

	len = sz_rfbSetColourMapEntriesMsg;

	for (i = 0; i < nColours; i++) {
		if(i<(int)cm->count) {
			if(cm->is16) {
				rgb[i*3] = Swap16IfLE(cm->data.shorts[i*3]);
				rgb[i*3+1] = Swap16IfLE(cm->data.shorts[i*3+1]);
				rgb[i*3+2] = Swap16IfLE(cm->data.shorts[i*3+2]);
			} else {
				rgb[i*3] = Swap16IfLE((unsigned short)cm->data.bytes[i*3]);
				rgb[i*3+1] = Swap16IfLE((unsigned short)cm->data.bytes[i*3+1]);
				rgb[i*3+2] = Swap16IfLE((unsigned short)cm->data.bytes[i*3+2]);
			}
		}
	}

	len += nColours * 3 * 2;

	if (rfbWriteExact(cl, wbuf, len) < 0) {
		rfbLogPerror("rfbSendSetColourMapEntries: write");
		rfbCloseClient(cl);
		if (wbuf != buf) free(wbuf);
		return FALSE;
	}

	rfbStatRecordMessageSent(cl, rfbSetColourMapEntries, len, len);
	if (wbuf != buf) free(wbuf);
	return TRUE;
}

/*
 * rfbSendBell sends a Bell message to all the clients.
 */

void
rfbSendBell(rfbScreenInfoPtr rfbScreen)
{
	rfbClientIteratorPtr i;
	rfbClientPtr cl;
	rfbBellMsg b;

	i = rfbGetClientIterator(rfbScreen);
	while((cl=rfbClientIteratorNext(i))) {
		b.type = rfbBell;
		if (rfbWriteExact(cl, (char *)&b, sz_rfbBellMsg) < 0) {
			rfbLogPerror("rfbSendBell: write");
			rfbCloseClient(cl);
		}
	}
	rfbStatRecordMessageSent(cl, rfbBell, sz_rfbBellMsg, sz_rfbBellMsg);
	rfbReleaseClientIterator(i);
}


/*
 * rfbSendServerCutText sends a ServerCutText message to all the clients.
 */

void
rfbSendServerCutText(rfbScreenInfoPtr rfbScreen,char *str, int len)
{
	rfbClientPtr cl;
	rfbServerCutTextMsg sct;
	rfbClientIteratorPtr iterator;

	iterator = rfbGetClientIterator(rfbScreen);
	while ((cl = rfbClientIteratorNext(iterator)) != NULL) {
		sct.type = rfbServerCutText;
		sct.length = Swap32IfLE(len);
		if (rfbWriteExact(cl, (char *)&sct,
				sz_rfbServerCutTextMsg) < 0) {
			rfbLogPerror("rfbSendServerCutText: write");
			rfbCloseClient(cl);
			continue;
		}
		if (rfbWriteExact(cl, str, len) < 0) {
			rfbLogPerror("rfbSendServerCutText: write");
			rfbCloseClient(cl);
		}
		rfbStatRecordMessageSent(cl, rfbServerCutText, sz_rfbServerCutTextMsg+len, sz_rfbServerCutTextMsg+len);
	}
	rfbReleaseClientIterator(iterator);
}

/*****************************************************************************
 *
 * UDP can be used for keyboard and pointer events when the underlying
 * network is highly reliable.  This is really here to support ORL's
 * videotile, whose TCP implementation doesn't like sending lots of small
 * packets (such as 100s of pen readings per second!).
 */

static unsigned char ptrAcceleration = 50;

void
rfbNewUDPConnection(rfbScreenInfoPtr rfbScreen,
		int sock)
{
	if (write(sock, &ptrAcceleration, 1) < 0) {
		rfbLogPerror("rfbNewUDPConnection: write");
	}
}

/*
 * Because UDP is a message based service, we can't read the first byte and
 * then the rest of the packet separately like we do with TCP.  We will always
 * get a whole packet delivered in one go, so we ask read() for the maximum
 * number of bytes we can possibly get.
 */

void
rfbProcessUDPInput(rfbScreenInfoPtr rfbScreen)
{
	int n;
	rfbClientPtr cl=rfbScreen->udpClient;
	rfbClientToServerMsg msg;

	if((!cl) || cl->onHold)
		return;

	if ((n = read(rfbScreen->udpSock, (char *)&msg, sizeof(msg))) <= 0) {
		if (n < 0) {
			rfbLogPerror("rfbProcessUDPInput: read");
		}
		rfbDisconnectUDPSock(rfbScreen);
		return;
	}

	switch (msg.type) {

	case rfbKeyEvent:
		if (n != sz_rfbKeyEventMsg) {
			rfbErr("rfbProcessUDPInput: key event incorrect length\n");
			rfbDisconnectUDPSock(rfbScreen);
			return;
		}
		cl->screen->kbdAddEvent(msg.ke.down, (rfbKeySym)Swap32IfLE(msg.ke.key), cl);
		break;

	case rfbPointerEvent:
		if (n != sz_rfbPointerEventMsg) {
			rfbErr("rfbProcessUDPInput: ptr event incorrect length\n");
			rfbDisconnectUDPSock(rfbScreen);
			return;
		}
		cl->screen->ptrAddEvent(msg.pe.buttonMask,
				Swap16IfLE(msg.pe.x), Swap16IfLE(msg.pe.y), cl);
		break;

	default:
		rfbErr("rfbProcessUDPInput: unknown message type %d\n",
				msg.type);
		rfbDisconnectUDPSock(rfbScreen);
	}
}


